lixing преди 3 години
ревизия
1f29ecca34
променени са 100 файла, в които са добавени 8525 реда и са изтрити 0 реда
  1. 9 0
      .gitignore
  2. 22 0
      README.md
  3. 8 0
      apm-common/README.md
  4. 116 0
      apm-common/pom.xml
  5. 32 0
      apm-common/src/main/java/com/persagy/apm/common/config/CommonWebConfigurer.java
  6. 51 0
      apm-common/src/main/java/com/persagy/apm/common/config/FeignExceptionHandler.java
  7. 95 0
      apm-common/src/main/java/com/persagy/apm/common/config/JacksonConfig.java
  8. 131 0
      apm-common/src/main/java/com/persagy/apm/common/config/SwaggerDisplayConfig.java
  9. 15 0
      apm-common/src/main/java/com/persagy/apm/common/constant/AppContextConstants.java
  10. 152 0
      apm-common/src/main/java/com/persagy/apm/common/constant/CommonConstant.java
  11. 50 0
      apm-common/src/main/java/com/persagy/apm/common/constant/CommonMessage.java
  12. 22 0
      apm-common/src/main/java/com/persagy/apm/common/constant/PageQueryConstants.java
  13. 148 0
      apm-common/src/main/java/com/persagy/apm/common/constant/RegexConstants.java
  14. 40 0
      apm-common/src/main/java/com/persagy/apm/common/constant/enums/BoolEnum.java
  15. 49 0
      apm-common/src/main/java/com/persagy/apm/common/constant/enums/DictTypeEnum.java
  16. 59 0
      apm-common/src/main/java/com/persagy/apm/common/constant/enums/IdPrefixEnum.java
  17. 59 0
      apm-common/src/main/java/com/persagy/apm/common/constant/enums/ObjTagEnum.java
  18. 53 0
      apm-common/src/main/java/com/persagy/apm/common/constant/enums/ResultEnum.java
  19. 40 0
      apm-common/src/main/java/com/persagy/apm/common/constant/enums/ValidEnum.java
  20. 117 0
      apm-common/src/main/java/com/persagy/apm/common/context/AppContext.java
  21. 28 0
      apm-common/src/main/java/com/persagy/apm/common/context/DefaultAppContext.java
  22. 27 0
      apm-common/src/main/java/com/persagy/apm/common/context/DefaultAppContextFactory.java
  23. 21 0
      apm-common/src/main/java/com/persagy/apm/common/context/IAppContextFactory.java
  24. 143 0
      apm-common/src/main/java/com/persagy/apm/common/exception/ApmExceptionHandler.java
  25. 14 0
      apm-common/src/main/java/com/persagy/apm/common/exception/RpcException.java
  26. 70 0
      apm-common/src/main/java/com/persagy/apm/common/handler/AppContextHandler.java
  27. 103 0
      apm-common/src/main/java/com/persagy/apm/common/helper/ExecutorHelper.java
  28. 295 0
      apm-common/src/main/java/com/persagy/apm/common/helper/LockHelper.java
  29. 995 0
      apm-common/src/main/java/com/persagy/apm/common/helper/RedisHelper.java
  30. 153 0
      apm-common/src/main/java/com/persagy/apm/common/helper/SpringHelper.java
  31. 615 0
      apm-common/src/main/java/com/persagy/apm/common/lang/PsDate.java
  32. 326 0
      apm-common/src/main/java/com/persagy/apm/common/lang/PsDateTime.java
  33. 271 0
      apm-common/src/main/java/com/persagy/apm/common/lang/PsTime.java
  34. 19 0
      apm-common/src/main/java/com/persagy/apm/common/model/annotation/SwaggerDisplayEnum.java
  35. 228 0
      apm-common/src/main/java/com/persagy/apm/common/model/data/DataType.java
  36. 27 0
      apm-common/src/main/java/com/persagy/apm/common/model/dto/DmpBaseQueryDTO.java
  37. 16 0
      apm-common/src/main/java/com/persagy/apm/common/model/dto/DmpOrderDTO.java
  38. 22 0
      apm-common/src/main/java/com/persagy/apm/common/model/dto/DmpRequiredParam.java
  39. 23 0
      apm-common/src/main/java/com/persagy/apm/common/model/dto/EnumQueryDTO.java
  40. 50 0
      apm-common/src/main/java/com/persagy/apm/common/model/dto/PageDTO.java
  41. 26 0
      apm-common/src/main/java/com/persagy/apm/common/model/dto/Sort.java
  42. 54 0
      apm-common/src/main/java/com/persagy/apm/common/model/entity/AuditableEntity.java
  43. 106 0
      apm-common/src/main/java/com/persagy/apm/common/model/entity/BaseEntity.java
  44. 59 0
      apm-common/src/main/java/com/persagy/apm/common/model/entity/IAuditableEntity.java
  45. 49 0
      apm-common/src/main/java/com/persagy/apm/common/model/entity/IBaseEntity.java
  46. 36 0
      apm-common/src/main/java/com/persagy/apm/common/model/entity/ITreeEntity.java
  47. 106 0
      apm-common/src/main/java/com/persagy/apm/common/model/query/ChainLogicExpression.java
  48. 238 0
      apm-common/src/main/java/com/persagy/apm/common/model/query/Condition.java
  49. 29 0
      apm-common/src/main/java/com/persagy/apm/common/model/query/ExpressionType.java
  50. 19 0
      apm-common/src/main/java/com/persagy/apm/common/model/query/IExpression.java
  51. 109 0
      apm-common/src/main/java/com/persagy/apm/common/model/query/LogicExpression.java
  52. 161 0
      apm-common/src/main/java/com/persagy/apm/common/model/query/Operator.java
  53. 64 0
      apm-common/src/main/java/com/persagy/apm/common/model/query/SqlExpression.java
  54. 255 0
      apm-common/src/main/java/com/persagy/apm/common/model/query/ValueExpression.java
  55. 30 0
      apm-common/src/main/java/com/persagy/apm/common/model/vo/BaseVo.java
  56. 18 0
      apm-common/src/main/java/com/persagy/apm/common/model/vo/DuplicateVO.java
  57. 23 0
      apm-common/src/main/java/com/persagy/apm/common/model/vo/EnumVO.java
  58. 73 0
      apm-common/src/main/java/com/persagy/apm/common/model/vo/GeneralDictVo.java
  59. 80 0
      apm-common/src/main/java/com/persagy/apm/common/model/vo/OldResponseResult.java
  60. 26 0
      apm-common/src/main/java/com/persagy/apm/common/model/vo/SimpleObjVO.java
  61. 26 0
      apm-common/src/main/java/com/persagy/apm/common/response/ApmDmpResponseUtil.java
  62. 23 0
      apm-common/src/main/java/com/persagy/apm/common/response/ApmResponseUpsertVO.java
  63. 61 0
      apm-common/src/main/java/com/persagy/apm/common/response/CommonResult.java
  64. 31 0
      apm-common/src/main/java/com/persagy/apm/common/response/DmpPageResponse.java
  65. 48 0
      apm-common/src/main/java/com/persagy/apm/common/response/FeignResponseContent.java
  66. 40 0
      apm-common/src/main/java/com/persagy/apm/common/response/FeignResponseItem.java
  67. 32 0
      apm-common/src/main/java/com/persagy/apm/common/response/FeignResponseMsg.java
  68. 26 0
      apm-common/src/main/java/com/persagy/apm/common/response/PageList.java
  69. 23 0
      apm-common/src/main/java/com/persagy/apm/common/serializer/PsDateDeserializer.java
  70. 22 0
      apm-common/src/main/java/com/persagy/apm/common/serializer/PsDateSerializer.java
  71. 23 0
      apm-common/src/main/java/com/persagy/apm/common/serializer/PsDateTimeDeserializer.java
  72. 22 0
      apm-common/src/main/java/com/persagy/apm/common/serializer/PsDateTimeSerializer.java
  73. 24 0
      apm-common/src/main/java/com/persagy/apm/common/serializer/PsTimeDeserializer.java
  74. 22 0
      apm-common/src/main/java/com/persagy/apm/common/serializer/PsTimeSerializer.java
  75. 95 0
      apm-common/src/main/java/com/persagy/apm/common/service/DownloadService.java
  76. 28 0
      apm-common/src/main/java/com/persagy/apm/common/utils/DmpExceptionUtil.java
  77. 34 0
      apm-common/src/main/java/com/persagy/apm/common/utils/DmpUpsertResultUtil.java
  78. 119 0
      apm-common/src/main/java/com/persagy/apm/common/utils/FileUtil.java
  79. 78 0
      apm-common/src/main/java/com/persagy/apm/common/utils/ListUtil.java
  80. 127 0
      apm-common/src/main/java/com/persagy/apm/common/utils/ProxyBeanUtil.java
  81. 97 0
      apm-common/src/main/java/com/persagy/apm/common/utils/ResultHelper.java
  82. 144 0
      apm-common/src/main/java/com/persagy/apm/common/utils/SecureAES.java
  83. 338 0
      apm-common/src/main/java/com/persagy/apm/common/utils/TypeConvertUtil.java
  84. 12 0
      apm-common/src/main/java/com/persagy/apm/common/validator/Delete.java
  85. 12 0
      apm-common/src/main/java/com/persagy/apm/common/validator/Insert.java
  86. 12 0
      apm-common/src/main/java/com/persagy/apm/common/validator/Query.java
  87. 12 0
      apm-common/src/main/java/com/persagy/apm/common/validator/Update.java
  88. 176 0
      apm-mybatis/README.md
  89. 35 0
      apm-mybatis/pom.xml
  90. 16 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/annotations/FormatMybatisQueryParams.java
  91. 68 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/aop/FormatMybatisQueryParamsAspect.java
  92. 26 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/config/MyBatisWebConfigurer.java
  93. 36 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/config/MybatisPlusConfig.java
  94. 17 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/dao/DbDao.java
  95. 30 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/dao/SimpleTreeCodeDao.java
  96. 40 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/handler/CommonMetaObjectHandler.java
  97. 46 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/handler/DbJsonTypeHandler.java
  98. 92 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/handler/DynamicDataSourceHandler.java
  99. 137 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/helper/TreeCodeHelper.java
  100. 0 0
      apm-mybatis/src/main/java/com/persagy/apm/mybatis/service/IDbService.java

+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+target/
+.settings/
+.project
+.classpath
+*.iml
+*.log
+*.versionsBackup
+.idea/
+/bin/

+ 22 - 0
README.md

@@ -0,0 +1,22 @@
+apm基础组件
+============ 
+
+模块
+---------------
+* apm-parent 包管理模块
+* [apm-common](/apm-common/README.md) 基础工具
+* [eaapm-server](/apm-server/README.md) 开发web服务模块
+
+最新变化
+---------------
+* 开发方式采用各业务的微服务,采用独立发布的方式,版本由服务自己控制
+
+环境搭建
+---------------
+
+开发示例
+---------------
+
+相关文档
+---------------
+

+ 8 - 0
apm-common/README.md

@@ -0,0 +1,8 @@
+apm-common 通用工具
+============ 
+
+说明
+---------------
+
+最新变化
+---------------

+ 116 - 0
apm-common/pom.xml

@@ -0,0 +1,116 @@
+<?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>apm-parent</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>3.0.0</version>
+        <relativePath>../apm-parent</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>apm-common</artifactId>
+    <version>3.0.0</version>
+    <repositories>
+        <repository>
+            <id>persagy</id>
+            <name>releases Repository</name>
+            <url>http://47.93.132.139:8081/nexus/content/repositories/persagy/</url>
+        </repository>
+        <repository>
+            <id>integrated</id>
+            <name>releases Repository</name>
+            <url>http://47.93.132.139:8081/nexus/content/repositories/integrated/</url>
+        </repository>
+    </repositories>
+    <distributionManagement>
+        <repository>
+            <id>persagy</id>
+            <name>releases Repository</name>
+            <url>http://47.93.132.139:8081/nexus/content/repositories/persagy/</url>
+            <layout>default</layout>
+        </repository>
+    </distributionManagement>
+    <dependencies>
+        <!-- 集成框架工具类 -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>integrated-common-core</artifactId>
+        </dependency>
+
+        <!-- 集成框架安全校验类 -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>integrated-common-security</artifactId>
+        </dependency>
+
+        <!-- web -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!-- jackson -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>2.11.3</version>
+        </dependency>
+        <!-- google工具类(easyCode模板中使用到了其中的方法) -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <!-- mysql -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <!-- alibaba druid -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+        <!-- mapstruct 优雅的进行bean与dto的转换 -->
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-jdk8</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
+        </dependency>
+        <!-- mybatisPlus相关的依赖 end -->
+
+        <!-- redis -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>integrated-redis-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- 拼音库 -->
+        <dependency>
+            <groupId>io.github.biezhi</groupId>
+            <artifactId>TinyPinyin</artifactId>
+            <version>2.0.3.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-spi</artifactId>
+            <version>2.9.2</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>1.5.22</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-common</artifactId>
+            <version>2.9.2</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+</project>

+ 32 - 0
apm-common/src/main/java/com/persagy/apm/common/config/CommonWebConfigurer.java

@@ -0,0 +1,32 @@
+package com.persagy.apm.common.config;
+
+import com.persagy.apm.common.handler.AppContextHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @description:
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/8 10:06 上午
+ * @version: V1.0
+ **/
+@Configuration
+@Order(1)
+public class CommonWebConfigurer implements WebMvcConfigurer {
+
+    @Bean
+    public AppContextHandler appContextHandler() {
+        return new AppContextHandler();
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        // 设置拦截的路径、不拦截的路径、优先级等等
+        registry.addInterceptor(appContextHandler()).order(10).addPathPatterns("/**");
+    }
+
+}

+ 51 - 0
apm-common/src/main/java/com/persagy/apm/common/config/FeignExceptionHandler.java

@@ -0,0 +1,51 @@
+package com.persagy.apm.common.config;
+
+import com.persagy.apm.common.exception.RpcException;
+import com.persagy.apm.common.response.FeignResponseContent;
+import com.persagy.apm.common.response.FeignResponseItem;
+import com.persagy.apm.common.response.FeignResponseMsg;
+import com.persagy.common.enums.ResponseCode;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+
+/**
+ * feign调用接口,如果接口返回了报错信息,将报错信息抛出
+ *
+ * @author lixing
+ * @version V1.0 2021/3/24 4:55 下午
+ **/
+@Aspect
+public class FeignExceptionHandler {
+
+    @Pointcut("@within(org.springframework.cloud.openfeign.FeignClient)")
+    private void pointCutMethod() {
+    }
+
+    @AfterReturning(pointcut = "pointCutMethod()", returning = "res")
+    public void process(Object res) {
+        String respCode = null;
+        String resultMsg = null;
+        if (res instanceof FeignResponseMsg) {
+            FeignResponseMsg response = (FeignResponseMsg) res;
+            respCode = response.getRespCode();
+            resultMsg = response.getRespMsg();
+        }
+
+        if (res instanceof FeignResponseContent) {
+            FeignResponseContent response = (FeignResponseContent) res;
+            respCode = response.getRespCode();
+            resultMsg = response.getRespMsg();
+        }
+
+        if (res instanceof FeignResponseItem) {
+            FeignResponseItem response = (FeignResponseItem) res;
+            respCode = response.getRespCode();
+            resultMsg = response.getRespMsg();
+        }
+
+        if (!ResponseCode.A00000.getCode().equals(respCode)) {
+            throw new RpcException(resultMsg);
+        }
+    }
+}

+ 95 - 0
apm-common/src/main/java/com/persagy/apm/common/config/JacksonConfig.java

@@ -0,0 +1,95 @@
+package com.persagy.apm.common.config;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Jackson统一处理返回值
+ *
+ * @author lixing
+ * @version V1.0 2021/4/20 10:15 上午
+ */
+@Configuration
+public class JacksonConfig {
+    @Bean
+    @Primary
+    @ConditionalOnMissingBean(ObjectMapper.class)
+    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
+        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
+        objectMapper.setSerializerFactory(objectMapper.getSerializerFactory()
+                .withSerializerModifier(new MyBeanSerializerModifier()));
+        return objectMapper;
+    }
+
+    public static class MyNullArrayJsonSerializer extends JsonSerializer {
+        @Override
+        public void serialize(Object value, JsonGenerator generator, SerializerProvider provider) throws IOException {
+            if (value == null) {
+                generator.writeStartArray();
+                generator.writeEndArray();
+            }
+        }
+    }
+
+    public static class MyNullObjJsonSerializer extends JsonSerializer {
+        @Override
+        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
+                throws IOException {
+            jsonGenerator.writeStartObject();
+            jsonGenerator.writeEndObject();
+        }
+    }
+
+    public static class MyBeanSerializerModifier extends BeanSerializerModifier {
+        // 数组,集合类型 null -> []
+        private JsonSerializer nullArrayJsonSerializer = new MyNullArrayJsonSerializer();
+        // 字符串等类型 null -> ""
+        private JsonSerializer nullObjJsonSerializer = new MyNullObjJsonSerializer();
+
+        @Override
+        public List<BeanPropertyWriter> changeProperties(
+                SerializationConfig config, BeanDescription beanDesc,
+                List beanProperties) {
+            for (Object beanProperty : beanProperties) {
+                BeanPropertyWriter writer = (BeanPropertyWriter) beanProperty;
+                //判断字段的类型,如果是array,list,set则注册nullSerializer
+                if (isArrayType(writer)) {
+                    writer.assignNullSerializer(this.nullArrayJsonSerializer);
+                } else if (!isBasicType(writer)){
+                    writer.assignNullSerializer(this.nullObjJsonSerializer);
+                }
+            }
+            return beanProperties;
+        }
+
+        boolean isArrayType(BeanPropertyWriter writer) {
+            Class clazz = writer.getPropertyType();
+            return clazz.isArray() || clazz.equals(List.class) || clazz.equals(Set.class);
+        }
+
+        boolean isBasicType(BeanPropertyWriter writer) {
+            Class clazz = writer.getPropertyType();
+            return clazz.equals(Integer.class) ||
+                    clazz.equals(Long.class) ||
+                    clazz.equals(Double.class) ||
+                    clazz.equals(Float.class) ||
+                    clazz.equals(Short.class) ||
+                    clazz.equals(Boolean.class) ||
+                    clazz.equals(Byte.class) ||
+                    clazz.equals(Date.class) ||
+                    clazz.equals(String.class);
+        }
+    }
+}

+ 131 - 0
apm-common/src/main/java/com/persagy/apm/common/config/SwaggerDisplayConfig.java

@@ -0,0 +1,131 @@
+package com.persagy.apm.common.config;
+
+import com.fasterxml.classmate.ResolvedType;
+import com.google.common.base.Optional;
+import com.persagy.apm.common.model.annotation.SwaggerDisplayEnum;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ReflectionUtils;
+import springfox.documentation.builders.ModelPropertyBuilder;
+import springfox.documentation.schema.Annotations;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
+import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
+import springfox.documentation.swagger.schema.ApiModelProperties;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * swagger注释自动加载枚举类型
+ *
+ * @author lixing
+ * @version V1.0 2021/4/12 10:43 上午
+ **/
+@Component
+@Primary
+@Slf4j
+public class SwaggerDisplayConfig implements ModelPropertyBuilderPlugin {
+
+
+    @Override
+    public void apply(ModelPropertyContext context) {
+        //获取当前字段的类型
+        final Class fieldType = context.getBeanPropertyDefinition().get().getField().getRawType();
+
+        //为枚举字段设置注释
+        descForEnumFields(context, fieldType);
+    }
+
+    /**
+     * 为枚举字段设置注释
+     */
+    private void descForEnumFields(ModelPropertyContext context, Class fieldType) {
+        Optional<ApiModelProperty> annotation = Optional.absent();
+
+        if (context.getAnnotatedElement().isPresent()) {
+            annotation = annotation
+                    .or(ApiModelProperties.findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
+        }
+        if (context.getBeanPropertyDefinition().isPresent()) {
+            annotation = annotation.or(Annotations.findPropertyAnnotation(
+                    context.getBeanPropertyDefinition().get(),
+                    ApiModelProperty.class));
+        }
+
+        //没有@ApiModelProperty 或者 notes 属性没有值,直接返回
+        if (!annotation.isPresent() || StringUtils.isBlank((annotation.get()).notes())) {
+            return;
+        }
+
+        //@ApiModelProperties中的notes指定的class类型
+        Class rawPrimaryType;
+        try {
+            rawPrimaryType = Class.forName((annotation.get()).notes());
+        } catch (ClassNotFoundException e) {
+            //如果指定的类型无法转化,直接忽略
+            return;
+        }
+
+        //如果对应的class是一个@SwaggerDisplayEnum修饰的枚举类,获取其中的枚举值
+        Object[] subItemRecords = null;
+        SwaggerDisplayEnum swaggerDisplayEnum = AnnotationUtils
+                .findAnnotation(rawPrimaryType, SwaggerDisplayEnum.class);
+        if (null != swaggerDisplayEnum && Enum.class.isAssignableFrom(rawPrimaryType)) {
+            subItemRecords = rawPrimaryType.getEnumConstants();
+        }
+        if (null == subItemRecords) {
+            return;
+        }
+
+
+        final List<String> displayValues = Arrays.stream(subItemRecords).filter(Objects::nonNull).map(item -> {
+            String typeName = swaggerDisplayEnum.type();
+            String descName = swaggerDisplayEnum.desc();
+
+            Object type = null;
+            Object desc = null;
+
+            Field typeField = ReflectionUtils.findField(item.getClass(), typeName);
+            if (typeField != null) {
+                ReflectionUtils.makeAccessible(typeField);
+                type = ReflectionUtils.getField(typeField, item);
+            }
+
+            Field descField = ReflectionUtils.findField(item.getClass(), descName);
+            if (descField != null) {
+                ReflectionUtils.makeAccessible(descField);
+                desc = ReflectionUtils.getField(descField, item);
+            }
+            type = type == null ? "" : type;
+            desc = desc == null ? "" : desc;
+            return type + ":" + desc;
+        }).filter(Objects::nonNull).collect(Collectors.toList());
+
+        String joinText = " (" + String.join("; ", displayValues) + ")";
+        try {
+            Field mField = ModelPropertyBuilder.class.getDeclaredField("description");
+            mField.setAccessible(true);
+            joinText = mField.get(context.getBuilder()) + joinText;
+        } catch (Exception e) {
+            log.error(e.getMessage());
+        }
+
+        final ResolvedType resolvedType = context.getResolver().resolve(fieldType);
+        context.getBuilder().description(joinText).type(resolvedType);
+    }
+
+
+    @Override
+    public boolean supports(DocumentationType documentationType) {
+        return true;
+    }
+
+}

+ 15 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/AppContextConstants.java

@@ -0,0 +1,15 @@
+package com.persagy.apm.common.constant;
+
+/**
+ * 必填参数常量类
+ *
+ * @author lixing
+ * @version V1.0 2021/3/25 5:50 下午
+ **/
+public class AppContextConstants {
+    public static final String GROUP_CODE = "groupCode";
+    public static final String APP_ID = "appId";
+    public static final String ACCOUNT_ID = "accountId";
+    public static final String PROJECT_ID = "projectId";
+
+}

+ 152 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/CommonConstant.java

@@ -0,0 +1,152 @@
+package com.persagy.apm.common.constant;
+/***
+ * Description: 通用常量类
+ * @author : lijie
+ * @date :2021/4/27 17:13
+ * Update By lijie 2021/4/27 17:13
+ */
+public class CommonConstant {
+    /**uploadFlag,响应头的key:1-成功,0-失败*/
+    public static final String UPLOAD_FLAG = "uploadFlag";
+    /**Excel文件的文件后缀*/
+    public static final String EXCEL_SUFFIX = "xlsx";
+    /**user_id:用户id*/
+    public static final String USER_ID="user_id";
+    /**dict_type:字典类型*/
+    public static final String DICT_TYPE="dict_type";
+    /**systemId:老项目中预留的系统id*/
+    public static final String SYSTEM_ID="systemId";
+    /**响应头:Access-Control-Expose-Headers*/
+    public static final String REPONSE_HEAD_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
+    /**响应头:Content-disposition*/
+    public static final String REPONSE_HEAD_CONTENT_DISPOSITION = "Content-disposition";
+    /**响应头:Content-Type*/
+    public static final String REPONSE_HEAD_CONTENT_TYPE = "Content-Type";
+    /**响应头:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet*/
+    public static final String REPONSE_HEAD_CONTENT_TYPE_SHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+    /**响应头:attachment;filename=*/
+    public static final String REPONSE_HEAD_CONTENT_DISPOSITION_ATTACHMENT = "attachment;filename=";
+    /**系统类型-windows*/
+    public static final String SYSTEM_TYPE_WINDOWS = "windows";
+    /**系统类型-linux/unix*/
+    public static final String SYSTEM_TYPE_LINUX = "linux/unix";
+    /**系统类型-mac*/
+    public static final String SYSTEM_TYPE_MAC = "mac";
+    /**自定义请求头-os-type*/
+    public static final String OS_TYPE = "os-type";
+    //数据字典类型-专业、系统大类、设备大类
+    public final static String dict_type_object_domain = "domain_require";
+    //数据字典类型-信息点
+    public final static String dict_type_info_point = "info_point";
+
+    //字段名称-数据平台
+    public final static String info_name_datas = "infos";
+    //字段名称-本地名称-项目
+    public final static String info_name_local_name_project = "ProjLocalName";
+    //字段名称-本地名称-项目
+    public final static String info_name_local_id_project = "ProjLocalID";
+    //字段名称-本地名称-建筑
+    public final static String info_name_local_name_build = "BuildLocalName";
+    //字段名称-本地编码-建筑
+    public final static String info_name_local_id_build = "BuildLocalID";
+    //字段名称-本地名称-楼层
+    public final static String info_name_local_name_floor = "FloorLocalName";
+    //字段名称-本地编码-楼层
+    public final static String info_name_local_id_floor = "FloorLocalID";
+    //字段名称-本地编码-竖井
+    public final static String info_name_local_id_shaft = "ShaftLocalID";
+    //字段名称-本地编码-竖井
+    public final static String info_name_local_name_shaft = "ShaftLocalName";
+    //字段名称-本地名称-空间
+    public final static String info_name_local_name_space = "RoomLocalName";
+    //字段名称-本地编码-空间
+    public final static String info_name_local_id_space = "RoomLocalID";
+    //字段名称-本地名称-系统
+    public final static String info_name_local_name_system = "SysLocalName";
+    //字段名称-本地编码-系统
+    public final static String info_name_local_id_system = "SysLocalID";
+    //字段名称-本地名称-设备
+    public final static String info_name_local_name_equip = "EquipLocalName";
+    //字段名称-本地编码-设备
+    public final static String info_name_local_id_equip = "EquipLocalID";
+    //字段名称-信息点-code
+    public final static String info_name_info_point_code = "infoPointCode";
+    //字段名称-信息点-name
+    public final static String info_name_info_point_name = "infoPointName";
+    //字段名称-信息点-输入方式(原始组件)
+    public final static String info_name_info_point_baseCmpt = "inputMode";
+
+    //关系-关联code
+    public final static String relation_from_id = "from_id";
+    //关系-被关联code
+    public final static String relation_to_id = "to_id";
+
+    //图类型-设备所在空间
+    public final static String graph_type_equip_in_space = "EquipinSpace";
+    //图类型-设备服务空间
+    public final static String graph_type_equip_service_space = "EquipforSpace";
+    //图类型-系统包含设备
+    public final static String graph_type_system_contain_equip = "SystemEquip";
+    //竖井关联的建筑
+    public final static String graph_type_shaft_and_building = "ShaftandBuilding";
+
+    //关系类型-设备所在空间
+    public final static String relation_type_equip_in_space = "1";
+    //关系类型-设备服务空间
+    public final static String relation_type_equip_service_space = "1";
+    //关系类型-系统包含设备
+    public final static String relation_type_system_contain_equip = "1";
+
+    //数据类型-建筑
+    public final static String data_type_build = "build";
+    //数据类型-楼层
+    public final static String data_type_floor = "floor";
+    //数据类型-空间
+    public final static String data_type_space = "space";
+    //数据类型-系统
+    public final static String data_type_system = "system";
+    //数据类型-设备
+    public final static String data_type_equip = "equip";
+    //数据类型-系统大类
+    public final static String data_type_system_class = "system_class";
+    //数据类型-设备大类
+    public final static String data_type_equip_class = "equip_class";
+    //数据类型-空间大类
+    public final static String data_type_space_class = "space_class";
+    //数据类型-建筑功能类型
+    public final static String data_type_build_func_type = "build_func_type";
+    //数据类型-楼层类型
+    public final static String data_type_floor_type = "floor_type";
+    //数据类型-竖井功能类型
+    public final static String data_type_shaft_type = "shaft_type";
+    //数据类型-竖井
+    public final static String data_type_shaft = "shaft";
+    //标识-项目
+    public final static String tag_project = "Pj";
+    //标识-建筑
+    public final static String tag_build = "Bd";
+    //标识-楼层
+    public final static String tag_floor = "Fl";
+    //标识-空间
+    public final static String tag_space = "Sp";
+    //标识-设备
+    public final static String tag_equip = "Eq";
+    //标识-系统
+    public final static String tag_system = "Sy";
+    //标识-设备对象
+    public final static String tag_equip_object = "EqObj";
+    //标识-系统对象
+    public final static String tag_system_object = "SyObj";
+    //标识-设备字典-专业
+    public final static String tag_dict_class = "Clazz";
+    //标识-设备字典-系统
+    public final static String tag_dict_sytstem = "System";
+    //标识-设备字典-设备
+    public final static String tag_dict_equip = "Equip";
+    //时间格式-显示
+    public final static String date_format_show = "yyyy-MM-dd HH:mm:ss";
+    //时间格式-存储
+    public final static String date_format_save = "yyyyMMddHHmmss";
+
+
+}

Файловите разлики са ограничени, защото са твърде много
+ 50 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/CommonMessage.java


+ 22 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/PageQueryConstants.java

@@ -0,0 +1,22 @@
+package com.persagy.apm.common.constant;
+
+/**
+ * 分页查询常量
+ *
+ * @author lixing
+ * @version V1.0 2021/3/24 3:05 下午
+ **/
+public class PageQueryConstants {
+    /**
+     * 默认查询第一页
+     */
+    public static final int DEFAULT_PAGE = 1;
+    /**
+     * 默认查询10条
+     */
+    public static final int DEFAULT_SIZE = 10;
+    /**
+     * list参数的最大size
+     */
+    public static final int PARAM_MAX_SIZE = 100;
+}

+ 148 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/RegexConstants.java

@@ -0,0 +1,148 @@
+package com.persagy.apm.common.constant;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 正则表达式常量
+ *
+ * @author lixing
+ * @version V1.0 2021/3/19 3:40 下午
+ **/
+public class RegexConstants {
+    /**
+     * 空字符串正则表达式
+     */
+    private static final String BLANK_REGEX = "^$";
+    /**
+     * 中文正则表达式
+     */
+    private static final String CH_REGEX = "[\u4e00-\u9fa5]";
+    /**
+     * 小写字母正则表达式
+     */
+    private static final String LOWERCASE_REGEX = "[a-z]";
+    /**
+     * 大写字母正则表达式
+     */
+    private static final String UPPERCASE_REGEX = "[A-Z]";
+    /**
+     * 数字正则表达式
+     */
+    private static final String NUM_REGEX = "[0-9]";
+    /**
+     * 经常允许的特殊字符正则表达式
+     */
+    private static final String CHAR_REGEX = "['._#~]";
+
+    /**
+     * 所有允许的特殊字符正则表达式
+     */
+    private static final String ALL_CHAR_REGEX = "[!\"#\\$%&'\\(\\)\\*\\+,-\\.\\/:;<=>\\?@\\[\\\\\\]\\^_`\\{\\|\\}~\\s]";
+
+    private static final String EN_REGEX_EXPRESSION = LOWERCASE_REGEX + "|" + UPPERCASE_REGEX;
+    private static final String EN_NUM_REGEX_EXPRESSION = EN_REGEX_EXPRESSION + "|" + NUM_REGEX;
+    private static final String EN_NUM_CHAR_REGEX_EXPRESSION = EN_NUM_REGEX_EXPRESSION + "|" + CHAR_REGEX;
+    private static final String NAME_REGEX_EXPRESSION = EN_NUM_CHAR_REGEX_EXPRESSION + "|" + CH_REGEX;
+    private static final String ALL_REGEX_EXPRESSION = EN_NUM_REGEX_EXPRESSION + "|" + CH_REGEX + "|" + ALL_CHAR_REGEX;
+    private static final String TEXT_REGEX_EXPRESSION = CH_REGEX + "|" + EN_REGEX_EXPRESSION;
+
+    /**
+     * 允许的字符正则表达式报警信息
+     */
+    private static final String CHAR_REGEX_WARNING = " ' . _  #  ~  ";
+    private static final String ALL_CHAR_REGEX_WARNING = "空格和! \" # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \\ ] ^ _` { | } ~";
+
+    /**
+     * 名称正则表达式
+     * 支持中英文字符,数字,"._#~"
+     */
+    public static final String NAME_REGEX = "(" + NAME_REGEX_EXPRESSION + ")+";
+
+
+    /**
+     * 名称正则表达式报警信息
+     */
+    public static final String NAME_REGEX_WARNING = "仅可输入中文,字母,数字以及" + CHAR_REGEX_WARNING;
+
+    /**
+     * 常用文本正则表达式
+     * 支持中英文字符
+     */
+    public static final String TEXT_REGEX = "(" + TEXT_REGEX_EXPRESSION + ")+";
+    /**
+     * 所有允许的文本正则表达式
+     * 支持中英文字符
+     */
+    public static final String ALL_REGEX = "(" + ALL_REGEX_EXPRESSION + ")+";
+
+    /**
+     * 文本正则表达式报警信息
+     */
+    public static final String TEXT_REGEX_WARNING = "仅可输入中文,字母";
+    public static final String ALL_REGEX_WARNING = "仅可输入中文,字母,数字以及" + ALL_CHAR_REGEX_WARNING;
+
+    /**
+     * 英文正则表达式
+     * 支持数字和字母
+     */
+    public static final String EN_NUM_REGEX = "(" + EN_NUM_REGEX_EXPRESSION + ")+";
+
+    /**
+     * 英文或空字符串正则表达式
+     * 支持数字和字母和空字符串
+     */
+    public static final String EN_NUM_BLANK_REGEX = "(" + BLANK_REGEX + "|" + EN_NUM_REGEX_EXPRESSION + ")+";
+    /**
+     * 英文正则表达式报警信息
+     */
+    public static final String EN_NUM_REGEX_WARNING = "仅可输入字母,数字";
+
+    /**
+     * 英文、数字、字符正则表达式
+     * 支持数字,字母,"._#~"
+     */
+    public static final String EN_NUM_CHAR_REGEX = "(" + EN_NUM_CHAR_REGEX_EXPRESSION + ")+";
+    /**
+     * 英文、数字、字符正则表达式报警信息
+     */
+    public static final String EN_NUM_CHAR_REGEX_WARNING = "仅可输入字母,数字以及" + CHAR_REGEX_WARNING;
+
+    /**
+     * 邮箱格式非法提示
+     */
+    public static final String EMAIL_REGEX_WARNING = "邮箱格式错误";
+
+    /**
+     * 身份证正则表达式
+     */
+    public static final String ID_CARD_REGEX = "(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)";
+    public static final String ID_CARD_OR_BLANK_REGEX = "(" +BLANK_REGEX + "|" + ID_CARD_REGEX + ")";
+
+    /**
+     * 身份证非法提示
+     */
+    public static final String ID_CARD_REGEX_WARNING = "请输入正确身份证号";
+
+    /**
+     * 手机号正则表达式
+     */
+    public static final String PHONE_REGEX = "^\\d{11}$";
+    public static final String PHONE_OR_BLANK_REGEX = "(" +BLANK_REGEX + "|" + PHONE_REGEX + ")";
+
+    /**
+     * 手机号非法提示
+     */
+    public static final String PHONE_REGEX_WARNING = "请输入正确手机号码";
+
+    public static void main(String[] args) {
+        String str = "12345678901";
+        String regEx = PHONE_REGEX;
+        // 编译正则表达式
+        Pattern pattern = Pattern.compile(regEx);
+        Matcher matcher = pattern.matcher(str);
+        // 字符串是否与正则表达式相匹配
+        boolean rs = matcher.matches();
+        System.out.println(rs);
+    }
+}

+ 40 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/enums/BoolEnum.java

@@ -0,0 +1,40 @@
+package com.persagy.apm.common.constant.enums;
+
+import com.persagy.apm.common.model.annotation.SwaggerDisplayEnum;
+
+/**
+ * @description: 合法标志
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/9 2:56 下午
+ * @version: V1.0
+ */
+@SwaggerDisplayEnum
+public enum BoolEnum {
+    FALSE("0", false),
+    TRUE("1", true);
+
+    private String type;
+    private Boolean desc;
+
+    BoolEnum(String type, Boolean desc) {
+        this.type = type;
+        this.desc = desc;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Boolean getDesc() {
+        return desc;
+    }
+
+    public void setDesc(Boolean desc) {
+        this.desc = desc;
+    }
+}

+ 49 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/enums/DictTypeEnum.java

@@ -0,0 +1,49 @@
+package com.persagy.apm.common.constant.enums;
+/***
+ * Description: 字典类型枚举类
+ * @author : lijie
+ * @date :2021/4/2 18:19
+ * Update By lijie 2021/4/2 18:19
+ */
+public enum DictTypeEnum {
+    /**工单控制模块*/
+    WO_CONTROL_MODULE("wo_control_module","工单控制模块"),
+    /**工单申请事项*/
+    WO_APPLAY_ITEM("wo_applay_item","工单申请事项"),
+    /**工单审批事项*/
+    WO_AUDIT_ITEM("wo_audit_item","工单审批事项"),
+    /**工单审核事项*/
+    WO_APPROVAL_ITEM("wo_approval_item","工单审核事项"),
+    /**工单时间类型*/
+    WO_EXECUTE_TYPE("wo_execute_type","工单时间类型"),
+    /**专业类型*/
+    DOMAIN_REQUIRE("domain_require","专业类型"),
+    /**工作类型:1-维保,2-维修,3-巡检,4-运行,5-安全*/
+    WORK_TYPE("work_type","工作类型:1-维保,2-维修,3-巡检,4-运行,5-安全"),
+    /**工单状态类型*/
+    WORK_ORDER_STATE("work_order_state","工单状态类型");
+
+    DictTypeEnum(String type, String describe) {
+        this.type = type;
+        this.describe = describe;
+    }
+
+    private String type;
+    private String describe;
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getDescribe() {
+        return describe;
+    }
+
+    public void setDescribe(String describe) {
+        this.describe = describe;
+    }
+}

+ 59 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/enums/IdPrefixEnum.java

@@ -0,0 +1,59 @@
+package com.persagy.apm.common.constant.enums;
+/***
+ * Description: 表ID的前缀枚举类
+ * @author : lijie
+ * @date :2021/4/1 22:17
+ * Update By lijie 2021/4/1 22:17
+ */
+public enum IdPrefixEnum {
+    /**sop表ID的前缀*/
+    SOP("SOP","sop表ID的前缀"),
+    /**sop_cell表ID的前缀*/
+    SOP_CELL("SOPSC","sop_cell表ID的前缀"),
+    /**sop_domain_rel表ID的前缀*/
+    SOP_DOMAIN_REL("SDRI","sop_domain_rel表ID的前缀"),
+    /**sop_tool_rel表ID的前缀*/
+    SOP_TOOL_REL("STRI","sop_tool_rel表ID的前缀"),
+    /**sop_media_rel表ID的前缀*/
+    SOP_MEDIA_REL("SMRI","sop_media_rel表ID的前缀"),
+    /**sop_obj_rel表ID的前缀*/
+    SOP_OBJ_REL("SOR","sop_obj_rel表ID的前缀"),
+    /**sop_equip_model_rel表id的前缀*/
+    SOP_EQUIP_MODEL_REL("SEMR","sop_equip_model_rel表ID的前缀"),
+    /**sop_order_type_rel表id的前缀*/
+    SOP_ORDER_TYPE_REL("SOTR","sop_order_type_rel表ID的前缀"),
+    /**sop_label_rel表id的前缀*/
+    SOP_LABEL_REL("SLR","sop_label_rel表ID的前缀"),
+    /**sop_edit表id的前缀*/
+    SOP_EDIT("SE","sop_edit表ID的前缀"),
+    /**sop_his表id的前缀*/
+    SOP_HIS("SH","sop_his表ID的前缀"),
+    /**sop_destroy_msg表id的前缀*/
+    SOP_DESTROY_MSG("SDM","sop_destroy_msg表ID的前缀"),
+    /**wo_flow_plan表ID的前缀*/
+    WO_FLOW_PLAN("FP","wo_flow_plan表ID的前缀");
+
+    IdPrefixEnum(String prefix, String describe) {
+        this.prefix = prefix;
+        this.describe = describe;
+    }
+
+    private String prefix;
+    private String describe;
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    public String getDescribe() {
+        return describe;
+    }
+
+    public void setDescribe(String describe) {
+        this.describe = describe;
+    }
+}

+ 59 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/enums/ObjTagEnum.java

@@ -0,0 +1,59 @@
+package com.persagy.apm.common.constant.enums;
+/***
+ * Description: 物理世界对象的tag
+ * @author : lijie
+ * @date :2021/4/2 9:54
+ * Update By lijie 2021/4/2 9:54
+ */
+public enum ObjTagEnum {
+    /**项目*/
+    PROJECT("Pj","项目"),
+    /**建筑*/
+    BUILD("Bd","建筑"),
+    /**楼层*/
+    FLOOR("Fl","楼层"),
+    /**空间*/
+    SPACE("Sp","空间"),
+    /**系统*/
+    SYSTEM("Sy","系统"),
+    /**竖井*/
+    SHAFT("shaft","竖井"),
+    /**设备*/
+    EQUIP("Eq","设备"),
+    /**设备对象*/
+    EQUIP_OBJECT("EqObj","设备对象"),
+    /**设备对象*/
+    SYSTEM_OBJECT("SyObj","系统对象"),
+    /**设备类*/
+    EQUIP_CLASS("equip_class","设备类"),
+    /**设备字典-专业*/
+    DICT_CLASS("Clazz","设备字典-专业"),
+    /**设备字典-系统*/
+    DICT_SYSTEM("System","设备字典-系统"),
+    /**设备字典-设备*/
+    DICT_EQUIP("Equip","设备字典-设备");
+
+    ObjTagEnum(String type, String describe) {
+        this.type = type;
+        this.describe = describe;
+    }
+
+    private String type;
+    private String describe;
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getDescribe() {
+        return describe;
+    }
+
+    public void setDescribe(String describe) {
+        this.describe = describe;
+    }
+}

+ 53 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/enums/ResultEnum.java

@@ -0,0 +1,53 @@
+package com.persagy.apm.common.constant.enums;
+/***
+ * Description: 结果枚举
+ * @author : lijie
+ * @date :2021/4/2 18:19
+ * Update By lijie 2021/4/2 18:19
+ */
+public enum ResultEnum {
+    /**Result*/
+    RESULT("Result","结果key"),
+    /**success-成功*/
+    SUCCESS("success","成功"),
+    /**failure-失败*/
+    FAILURE("failure","失败"),
+    /**ResultMsg-错误消息*/
+    RESULT_MSG("ResultMsg","错误消息"),
+    /**内容数组*/
+    CONTENT("Content","内容数组"),
+    /**对象*/
+    ITEM("Item","对象"),
+    /**总数*/
+    COUNT("Count","总数"),
+    /**第六代框架接口返回结构*/
+    PERSAGY_RESULT("result","PERSAGY_RESULT"),
+    /**第六代框架接口返回结构*/
+    PERSAGY_REASON("reason","PERSAGY_RESULT"),
+    /**第六代框架接口返回结构*/
+    PERSAGY_CONTENT("content","PERSAGY_CONTENT");
+
+    ResultEnum(String type, String describe) {
+        this.type = type;
+        this.describe = describe;
+    }
+
+    private String type;
+    private String describe;
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getDescribe() {
+        return describe;
+    }
+
+    public void setDescribe(String describe) {
+        this.describe = describe;
+    }
+}

+ 40 - 0
apm-common/src/main/java/com/persagy/apm/common/constant/enums/ValidEnum.java

@@ -0,0 +1,40 @@
+package com.persagy.apm.common.constant.enums;
+
+import com.persagy.apm.common.model.annotation.SwaggerDisplayEnum;
+
+/**
+ * @description: 合法标志
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/9 2:56 下午
+ * @version: V1.0
+ */
+@SwaggerDisplayEnum
+public enum ValidEnum {
+    FALSE(0, false),
+    TRUE(1, true);
+
+    private Integer type;
+    private Boolean desc;
+
+    ValidEnum(Integer type, Boolean desc) {
+        this.type = type;
+        this.desc = desc;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    public Boolean getDesc() {
+        return desc;
+    }
+
+    public void setDesc(Boolean desc) {
+        this.desc = desc;
+    }
+}

+ 117 - 0
apm-common/src/main/java/com/persagy/apm/common/context/AppContext.java

@@ -0,0 +1,117 @@
+package com.persagy.apm.common.context;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+
+
+/**
+ * 应用访问上下文类,主要负责上下文的属性和参数的操作
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@Slf4j
+public abstract class AppContext implements Serializable {
+
+    /** 序列id */
+    private static final long serialVersionUID = 697592850956445675L;
+
+    /** 工厂对象 */
+    private static IAppContextFactory factory = null;
+
+    /**
+     * 取得当前线程中的上下文对象
+     * @return 上下文对象
+     */
+    public static AppContext getContext() {
+        return getFactory().getContext();
+    }
+
+    /**
+     * 反注册当前上下文对象
+     */
+    public static void unload() {
+        getFactory().unloadContext();
+    }
+
+    /**
+     * 取得上下文工厂类
+     * @return 上下文工厂类
+     */
+    public static IAppContextFactory getFactory() {
+        if (factory == null) {
+            factory = new DefaultAppContextFactory();
+        }
+        return factory;
+    }
+
+//    /**
+//     * 取得操作用户主键
+//     * @return 用户主键
+//     */
+//    public abstract String getUserId();
+//
+//    /**
+//     * 设置操作用户主键
+//     * @param userId 用户主键
+//     */
+//    public abstract void setUserId(String userId);
+//
+//    /**
+//     * 取得操作用户名
+//     * @return 用户名
+//     */
+//    public abstract String getUserName();
+//
+//    /**
+//     * 设置操作用户名
+//     * @param userName 用户名
+//     */
+//    public abstract void setUserName(String userName);
+//
+//    /**
+//     * 取得租户
+//     * @return 租户
+//     */
+//    public abstract String getTenant();
+//
+//    /**
+//     * 设置租户
+//     * @param tenant 租户
+//     */
+//    public abstract void setTenant(String tenant);
+//
+//    /**
+//     * 取得产品线
+//     * @return 产品线
+//     */
+//    public abstract String getProductLine();
+//
+//    /**
+//     * 设置产品线
+//     * @param productLine 产品线
+//     */
+//    public abstract void setProductLine(String productLine);
+
+    /**
+     * 动态数据源支持
+     * @return java.lang.String
+     **/
+    public abstract String getDataSourceName();
+
+    /**
+     * 设置动态数据源
+     * @param dataSourceName 动态数据源
+     * @return void
+     **/
+    public abstract void setDataSourceName(String dataSourceName);
+
+    public abstract void setAccountId(String accountId);
+    public abstract void setProjectId(String projectId);
+    public abstract void setGroupCode(String groupCode);
+    public abstract void setAppId(String appId);
+    public abstract String getAccountId();
+    public abstract String getProjectId();
+    public abstract String getGroupCode();
+    public abstract String getAppId();
+}

+ 28 - 0
apm-common/src/main/java/com/persagy/apm/common/context/DefaultAppContext.java

@@ -0,0 +1,28 @@
+package com.persagy.apm.common.context;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 默认应用上下文
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class DefaultAppContext extends AppContext {
+
+    /**
+     * 用户主键
+     */
+    private String accountId;
+
+    /**
+     * 数据源名称
+     */
+    private String dataSourceName;
+
+    private String projectId;
+    private String appId;
+    private String groupCode;
+}

+ 27 - 0
apm-common/src/main/java/com/persagy/apm/common/context/DefaultAppContextFactory.java

@@ -0,0 +1,27 @@
+package com.persagy.apm.common.context;
+
+/**
+ * 默认的appContext工厂
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public class DefaultAppContextFactory implements IAppContextFactory {
+
+    /** 线程级的上下文 */
+    private ThreadLocal<AppContext> context = new ThreadLocal<>();
+
+    @Override
+    public AppContext getContext() {
+        AppContext c = context.get();
+        if(c == null){
+            c = new DefaultAppContext();
+            context.set(c);
+        }
+        return c;
+    }
+
+    @Override
+    public void unloadContext() {
+        context.remove();
+    }
+}

+ 21 - 0
apm-common/src/main/java/com/persagy/apm/common/context/IAppContextFactory.java

@@ -0,0 +1,21 @@
+package com.persagy.apm.common.context;
+
+/**
+ * app上下问工厂类
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public interface IAppContextFactory {
+
+    /**
+     * 创建应用上下文对象
+     * @return 应用上下文对象
+     */
+    AppContext getContext();
+
+    /**
+     * 卸载应用上下文对象
+     * @return 应用上下文对象
+     */
+    void unloadContext();
+}

+ 143 - 0
apm-common/src/main/java/com/persagy/apm/common/exception/ApmExceptionHandler.java

@@ -0,0 +1,143 @@
+package com.persagy.apm.common.exception;
+
+import com.persagy.common.enums.ResponseCode;
+import com.persagy.common.exception.BusinessException;
+import com.persagy.apm.common.response.CommonResult;
+import com.persagy.apm.common.utils.ResultHelper;
+import com.persagy.security.exception.AESDecryptException;
+import feign.FeignException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.validation.BindException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.NoHandlerFoundException;
+
+import javax.validation.ValidationException;
+
+/**
+ * @description: 通用异常处理类
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/5 5:10 下午
+ * @version: V1.0
+ **/
+@RestControllerAdvice
+@Slf4j
+public class ApmExceptionHandler {
+    /**
+     * feign调用异常
+     */
+    @ExceptionHandler({RpcException.class})
+    public CommonResult handleRpcException(RpcException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.C0110.getCode(), e.getMessage());
+    }
+
+    /**
+     * token解析失败
+     */
+    @ExceptionHandler({AESDecryptException.class})
+    public CommonResult handleRpcException(AESDecryptException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.A0220.getCode(), e.getMessage());
+    }
+
+
+    /**
+     * 空指针异常
+     */
+    @ExceptionHandler(NullPointerException.class)
+    public CommonResult handleBusinessRuntimeException(NullPointerException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.B0001.getCode(), e.getMessage());
+    }
+
+    /**
+     * 自定义业务异常
+     */
+    @ExceptionHandler(BusinessException.class)
+    public CommonResult handleBusinessRuntimeException(BusinessException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(e.getErrorCode(), e.getErrorDesc());
+    }
+
+    /**
+     * 入参校验异常
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public CommonResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.A0400.getCode(), e.getBindingResult().getFieldError().getDefaultMessage());
+    }
+
+    /**
+     * 入参校验异常
+     */
+    @ExceptionHandler(BindException.class)
+    public CommonResult handleBindException(BindException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.A0400.getCode(), e.getBindingResult().getFieldError().getDefaultMessage());
+    }
+
+    /**
+     * 参数校验异常,message 为手动抛出时定义的具体异常信息
+     */
+    @ExceptionHandler(ValidationException.class)
+    public CommonResult handleValidationException(ValidationException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.A0400.getCode(), e.getCause().getMessage());
+    }
+
+    /**
+     * 参数校验异常,message 为手动抛出时定义的具体异常信息
+     */
+    @ExceptionHandler(IllegalArgumentException.class)
+    public CommonResult handleIllegalArgumentException(IllegalArgumentException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.A0400.getCode(), e.getMessage());
+    }
+
+    /**
+     * URI请求链接没有映射
+     */
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    @ExceptionHandler(NoHandlerFoundException.class)
+    public CommonResult handleNoHandlerFoundException(NoHandlerFoundException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.A0422.getCode(), e.getMessage());
+    }
+
+    /**
+     * Feign 调用异常
+     */
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    @ExceptionHandler(FeignException.class)
+    public CommonResult handleFeignException(FeignException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.C0110.getCode(), e.getMessage());
+    }
+
+    /**
+     *  body体未找到
+     */
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(HttpMessageNotReadableException.class)
+    public CommonResult handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.A0402.getCode(), ResponseCode.A0402.getDesc());
+    }
+
+    /**
+     * 系统默认繁忙
+     */
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    @ExceptionHandler(Exception.class)
+    public CommonResult handleException(Exception e) {
+        log.error(e.getMessage(), e);
+        return ResultHelper.failure(ResponseCode.Z9999.getCode(), ResponseCode.Z9999.getDesc());
+    }
+}

+ 14 - 0
apm-common/src/main/java/com/persagy/apm/common/exception/RpcException.java

@@ -0,0 +1,14 @@
+package com.persagy.apm.common.exception;
+
+/**
+ * @description:
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/8 11:39 上午
+ * @version: V1.0
+ **/
+public class RpcException extends RuntimeException{
+    public RpcException (String message) {
+        super(message);
+    }
+}

+ 70 - 0
apm-common/src/main/java/com/persagy/apm/common/handler/AppContextHandler.java

@@ -0,0 +1,70 @@
+package com.persagy.apm.common.handler;
+
+import com.alibaba.fastjson.JSONObject;
+import com.persagy.apm.common.constant.AppContextConstants;
+import com.persagy.apm.common.context.AppContext;
+import com.persagy.apm.common.context.DefaultAppContext;
+import com.persagy.apm.common.utils.SecureAES;
+import com.persagy.security.exception.AESDecryptException;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @description:
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/8 9:35 上午
+ * @version: V1.0
+ */
+public class AppContextHandler extends HandlerInterceptorAdapter {
+
+    /** 忽略的url - swagger的文档不校验 */
+    private static final String[] IGNORE_URL = {".html", ".js", ".css", "/swagger-resources"};
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String requestURI = request.getRequestURI();
+        if (StringUtils.containsAny(requestURI, IGNORE_URL)) {
+            return true;
+        }
+        ensureContextInfo(request.getHeader("token"));
+        return true;
+    }
+
+    /**
+     * 根据token获取上下文信息
+     * @param token
+     */
+    private void ensureContextInfo(String token){
+        // 从token中解析数据
+        SecureAES aes = new SecureAES("63499E35378AE1B0733E3FED7F780B68", "C0E7BD39B52A15C7");
+        JSONObject tokenObj = null;
+        try {
+            tokenObj = aes.decryptToken(token);
+        } catch (UnsupportedEncodingException e) {
+            throw new AESDecryptException("token解析异常");
+        }
+        // 获取值
+        String accountId = tokenObj.getString(AppContextConstants.ACCOUNT_ID);
+        String groupCode = tokenObj.getString(AppContextConstants.GROUP_CODE);
+        String appId = tokenObj.getString(AppContextConstants.APP_ID);
+        AppContext.getContext().setAccountId(accountId);
+        AppContext.getContext().setGroupCode(groupCode);
+        AppContext.getContext().setAppId(appId);
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+        DefaultAppContext.getFactory().unloadContext();
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        DefaultAppContext.getFactory().unloadContext();
+    }
+}

+ 103 - 0
apm-common/src/main/java/com/persagy/apm/common/helper/ExecutorHelper.java

@@ -0,0 +1,103 @@
+package com.persagy.apm.common.helper;
+
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.*;
+
+/**
+ * 线程池助手
+ * @author Charlie Yu
+ * @version 1.0 2021-03-02
+ */
+public final class ExecutorHelper {
+
+    /**
+     * 线程池
+     */
+    private static ExecutorService executor = null;
+
+    /**
+     * 通过线程池执行任务,无需同步
+     * @param run 可运行的对象
+     */
+    public static void execute(Runnable run) {
+        getExecutor().execute(run);
+    }
+
+    /**
+     * 通过线程池执行任务,并返回同步点
+     * @param task 线程对象
+     * @return 同步点对象,同步点只能返回null,
+     */
+    public static Future<?> submitRunable(Runnable task) {
+        return getExecutor().submit(task);
+    }
+
+    /**
+     * 通过线程池执行任务,并返回同步点
+     * @param task 可回调对象
+     * @return 同步点对象,可以取得异步返回结果,
+     */
+    public static <T> Future<T> submit(Callable<T> task) {
+        return getExecutor().submit(task);
+    }
+
+    /**
+     * 通过线程池执行任务列表,并返回同步点列表
+     * @param tasks 任务列表
+     * @return 同步点对象,可以取得异步返回结果,
+     * @throws InterruptedException 中断异常
+     */
+    public static <T> List<Future<T>> submitAll(Collection<Callable<T>> tasks) throws InterruptedException {
+        return getExecutor().invokeAll(tasks);
+    }
+
+    /**
+     * 通过线程池执行任务列表,并返回第一个成功的结果
+     * @param tasks 任务列表
+     * @param <T> 结果泛型
+     * @return 一个任务的结果对象
+     * @throws InterruptedException
+     * @throws ExecutionException
+     */
+    public static <T> T submitAny(Collection<Callable<T>> tasks) throws InterruptedException, ExecutionException {
+        return getExecutor().invokeAny(tasks);
+    }
+
+    /**
+     * 取得线程池
+     * @return 线程池服务
+     */
+    public static ExecutorService getExecutor() {
+        if (executor == null) {
+            init();
+        }
+        return executor;
+    }
+
+    /**
+     * 初始化线程池服务
+     */
+    public static synchronized void init() {
+        if (executor != null) {
+            return;
+        }
+        ThreadPoolTaskExecutor poolTaskExecutor = SpringHelper.getBean(
+                SpringHelper.getString("threadpool.name", "threadPoolTaskExecutor"), ThreadPoolTaskExecutor.class);
+        if (poolTaskExecutor != null) {
+            executor = poolTaskExecutor.getThreadPoolExecutor();
+        } else {
+            //改成无限长的线程池,看看有没有问题
+            executor = Executors.newCachedThreadPool();
+            //executor = Executors.newWorkStealingPool();
+
+            /*ThreadPoolExecutor executorTest = new ThreadPoolExecutor(10,
+                    AppConfig.getInstance().getInt("restcaller.poolnum", 20), 10, TimeUnit.SECONDS,
+                    new SynchronousQueue<Runnable>());
+            executorTest.allowCoreThreadTimeOut(true);
+            executor = executorTest;*/
+        }
+    }
+}

+ 295 - 0
apm-common/src/main/java/com/persagy/apm/common/helper/LockHelper.java

@@ -0,0 +1,295 @@
+package com.persagy.apm.common.helper;
+
+import com.persagy.common.exception.BusinessException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 锁助手
+ * @author Charlie Yu
+ * @version 1.0 2021-03-30
+ */
+@Slf4j
+public class LockHelper {
+
+    /**
+     * jedis模板
+     */
+    private static RedisTemplate redisTemplate = null;
+
+    /**
+     * 加锁标志
+     */
+    private static final byte[] LOCKED = new byte[]{'Y'};
+
+    /**
+     * 一毫秒等于的纳秒数
+     */
+    public static final long ONE_MILLI_NANOS = 1000000L;
+
+    /**
+     * 默认超时时间(毫秒)
+     */
+    public static final long DEFAULT_TIME_OUT = 3000;
+
+    /**
+     * 锁的超时时间(秒),过期删除
+     */
+    public static final int EXPIRE = 5 * 60;
+
+
+    /**线程变量*/
+    private static ThreadLocal<Map<String,Integer>> locks = new ThreadLocal<>();
+
+
+    /**
+     * 按键值增加锁
+     * @param key     锁的键值
+     * @param timeout 超时时长
+     * @param expire  锁过期时间
+     * @return 是否锁定成功
+     */
+    public static boolean lock(String key,long timeout,int expire){
+        //解决嵌套加锁
+        Map<String,Integer> ls = locks.get();
+        if(ls != null){
+            Integer c =  ls.get(key);
+            if(c != null){
+                c++;
+                ls.put(key,c);
+                return true;
+            }
+        }else{
+            ls = new HashMap<>(16);
+            locks.set(ls);
+        }
+        Boolean rs = (Boolean) getRedisTemplate().execute(new LockRedisCallback(key,timeout,expire));
+        boolean flag = rs!=null && rs;
+        if(flag){
+            ls.put(key,1);
+        }
+        return flag;
+    }
+
+    /**
+     * 按键值增加锁
+     * @param key     锁的键值
+     * @param timeout 超时时长
+     * @return 是否锁定成功
+     */
+    public static boolean lock(String key,long timeout){
+        return lock(key,timeout,EXPIRE);
+    }
+    /**
+     * 按键值增加锁
+     * @param key     锁的键值
+     * @return 是否锁定成功
+     */
+    public static boolean lock(String key){
+        return lock(key,DEFAULT_TIME_OUT);
+    }
+
+    /**
+     * 解锁指定的键值
+     */
+    public static void unlock(String key){
+        //解决嵌套加锁
+        Map<String,Integer> ls = locks.get();
+        if(ls == null) {
+            return;
+        }
+        Integer c =  ls.get(key);
+        if(c == null) {
+            return;
+        }
+        if(c > 1) {
+            c--;
+            ls.put(key,c);
+            return;
+        }
+        ls.remove(key);
+        if(ls.isEmpty()) {
+            locks.remove();
+        }
+
+        getRedisTemplate().execute(new UnLockRedisCallback(key));
+    }
+
+    /**
+     * 清除线程占用的所有的锁
+     */
+    public void clearLock(){
+        Map<String,Integer> ls = locks.get();
+        for(String key:ls.keySet()){
+            unlock(key);
+        }
+        locks.remove();
+    }
+
+    /**
+     * 排他独占锁
+     * @param key 独占键值
+     * @param expire 超期时长
+     * @return 布尔值
+     */
+    public static boolean exclusive(String key, int expire){
+        Boolean rs = (Boolean) getRedisTemplate().execute(new ExclusiveRedisCallback(key, expire));
+        return rs!=null && rs;
+    }
+
+    /**
+     * 排他独占锁
+     * @param key 键值
+     * @return 布尔值
+     */
+    public static boolean exclusive(String key){
+        return exclusive(key,EXPIRE);
+    }
+
+    /**
+     * 释放排他独占锁
+     * @param key 键值
+     */
+    public static void releaseExcusive(String key){
+        getRedisTemplate().execute(new UnLockRedisCallback(key));
+    }
+
+    /**
+     * 获取redis访问模板
+     * @return redis访问模板
+     */
+    public static RedisTemplate getRedisTemplate(){
+        if(redisTemplate == null) {
+            redisTemplate = SpringHelper.getBean("redisTemplate",RedisTemplate.class);
+            if(redisTemplate == null){
+                Map<String, RedisTemplate> beans = SpringHelper.getBeansOfType(RedisTemplate.class);
+                if(!beans.isEmpty()) {
+                    redisTemplate = beans.values().iterator().next();
+                }
+            }
+        }
+        if(redisTemplate == null) {
+            log.error("redis config is error! please init RedisTemplate bean!");
+            throw new RuntimeException("redis config is error! please init RedisTemplate bean!");
+        }
+        return redisTemplate;
+    }
+    /**
+     * 加锁回调指令
+     */
+    public static class LockRedisCallback implements RedisCallback<Boolean> {
+        private long timeout;
+        private byte[] key;
+        private int expire;
+
+        public LockRedisCallback(String key,long timeout,int expire) {
+            this.timeout = timeout;
+            this.expire = expire;
+            this.key = redisTemplate.getStringSerializer().serialize(key);
+        }
+
+        /**
+         * 加锁方法
+         * @param connection redis连接
+         * @return 布尔值
+         */
+        @Override
+        public Boolean doInRedis(RedisConnection connection) {
+            long nano = System.nanoTime();
+            timeout *= ONE_MILLI_NANOS;
+            try {
+                while ((System.nanoTime() - nano) < timeout) {
+                    connection.watch(key);
+                    // 开启watch之后,如果key的值被修改,则事务失败,exec方法返回null
+                    byte[] value = connection.get(key);
+                    if (value == null) {
+                        connection.multi();
+                        connection.setEx(key, expire, LOCKED);
+                        connection.get(key);
+                        List<Object> rs = connection.exec();
+                        if (rs != null && rs.size() > 0) {
+                            connection.unwatch();
+                            return true;
+                        }
+                    }
+                    connection.unwatch();
+                    // 短暂休眠,nano避免出现活锁
+                    Thread.sleep(10);
+                }
+            } catch (Exception e) {
+                log.error("lock error happened",e);
+            }
+            return false;
+        }
+    }
+
+    /**
+     * 解锁回调指令
+     */
+    public static class UnLockRedisCallback implements RedisCallback<Boolean> {
+        private byte[] key;
+
+        public UnLockRedisCallback(String key) {
+            try {
+                this.key = key.getBytes(StandardCharsets.UTF_8.name());
+            } catch (UnsupportedEncodingException e) {
+                throw new BusinessException(e);
+            }
+        }
+
+        @Override
+        public Boolean doInRedis(RedisConnection connection) {
+            connection.del(key);
+            return true;
+        }
+    }
+    /**
+     * 排他独占锁回调,无需解锁
+     */
+    public static class ExclusiveRedisCallback implements RedisCallback<Boolean> {
+        private byte[] key;
+        private int expire;
+
+        public ExclusiveRedisCallback(String key,int expire) {
+            this.expire = expire;
+            this.key = redisTemplate.getStringSerializer().serialize(key);
+        }
+
+        /**
+         * 加锁方法
+         * @param connection redis连接
+         * @return 布尔值
+         */
+        @Override
+        public Boolean doInRedis(RedisConnection connection) {
+            try {
+                connection.watch(key);
+                // 开启watch之后,如果key的值被修改,则事务失败,exec方法返回null
+                byte[] value = connection.get(key);
+                if (value != null) {
+                    return false;
+                }
+                connection.multi();
+                connection.setEx(key, expire, LOCKED);
+                connection.get(key);
+                List<Object> rs = connection.exec();
+                if (rs != null && rs.size() > 0) {
+                    return true;
+                }
+            } catch (Exception e) {
+                log.error("lock error happened",e);
+            } finally {
+                connection.unwatch();
+            }
+            return false;
+        }
+    }
+}

+ 995 - 0
apm-common/src/main/java/com/persagy/apm/common/helper/RedisHelper.java

@@ -0,0 +1,995 @@
+package com.persagy.apm.common.helper;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.SessionCallback;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.serializer.GenericToStringSerializer;
+import org.springframework.data.redis.serializer.RedisSerializer;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Redis工具类
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@Slf4j
+public final class RedisHelper {
+
+    /** jedis模板 */
+    private static RedisTemplate redisTemplate = null;
+
+    /** 助手类,私有构造方法 */
+    private RedisHelper() {
+    }
+
+    /**
+     * 获取缓存模板
+     * @return 获取缓存模板
+     */
+    public static RedisTemplate getTemplate() {
+        if (redisTemplate == null) {
+            redisTemplate = SpringHelper.getBean("redisTemplate", RedisTemplate.class);
+            if (redisTemplate == null) {
+                Map<String, RedisTemplate> beans = SpringHelper.getBeansOfType(RedisTemplate.class);
+                if (!beans.isEmpty()) {
+                    redisTemplate = beans.values().iterator().next();
+                }
+            }
+        }
+        if (redisTemplate == null) {
+            log.error("redis config is error! please init RedisTemplate bean!");
+            throw new RuntimeException("redis config is error! please init RedisTemplate bean!");
+        }
+        return redisTemplate;
+    }
+
+    /**
+     * 按键值模式删除指定键值
+     * @param keyMode 键值匹配模式
+     * @return 清楚记录数组
+     */
+    public static long clearKeys(String keyMode) {
+        String clearScript = "local keys = redis.call('keys', ARGV[1]) for i=1,#keys,5000 do redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) end return #keys";
+        return (long) getTemplate().execute(new DefaultRedisScript<>(clearScript, Long.class), getKeySerializer(), getValueSerializer(), new ArrayList(), keyMode);
+    }
+
+    /**
+     * 按键值模式删除指定键值
+     * @param keys 键值列表
+     * @return 清楚记录数
+     */
+    public static long clearKeys(List<String> keys) {
+        String clearScript = "return redis.call('del', unpack(KEY))";
+        return (long) getTemplate().execute(new DefaultRedisScript<>(clearScript, Long.class), getKeySerializer(), getValueSerializer(), keys, new Object[0]);
+    }
+
+    /**
+     * 指定键值累加操作
+     * @param key 键值
+     * @return 累加结果
+     */
+    public static Long incr(final String key) {
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.incr(keySerial(key)));
+    }
+
+    /**
+     * 指定键值累减操作
+     * @param key 键值
+     * @return 累减结果
+     */
+    public static Long decr(final String key) {
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.decr(keySerial(key)));
+    }
+
+    /**
+     * 按键值获取存储数据
+     * @param key 键值
+     * @param <T> 存储值泛型
+     * @return 存储值
+     */
+    public static <T> T cmdGet(final String key, Class<T> cl) {
+        return strDeserial(cl, getBytes(keySerial(key)));
+    }
+
+    /**
+     * 按键值获取存储数据
+     * @param key 键值
+     * @param <T> 存储值泛型
+     * @return 存储值
+     */
+    public static <T> T get(String key) {
+        return objDeserial(getBytes(keySerial(key)));
+    }
+
+    /**
+     * 按键值获取存储数据
+     * @param key 键值
+     * @return 存储值
+     */
+    public static byte[] getBytes(final byte[] key) {
+        return (byte[]) getTemplate().execute((RedisCallback<byte[]>) connection -> connection.get(key));
+    }
+
+    /**
+     * 按键值存储新的值并获取旧存储数据
+     * @param key   键值
+     * @param value 新存储值
+     * @param <T>   存储值泛型
+     * @return 旧存储值
+     */
+    public static <T> T cmdGetAndSet(String key, T value) {
+        Class<T> cl = (Class<T>) value.getClass();
+        return strDeserial(cl, getAndSetBytes(keySerial(key), strSerial(value)));
+    }
+
+    /**
+     * 按键值存储新的值并获取旧存储数据
+     * @param key   键值
+     * @param value 新存储值
+     * @param <T>   存储值泛型
+     * @return 旧存储值
+     */
+    public static <T> T getAndSet(String key, T value) {
+        return objDeserial(getAndSetBytes(keySerial(key), objSerial(value)));
+    }
+
+    /**
+     * 按键值存储新的值并获取旧存储数据
+     * @param key   键值
+     * @param value 新存储值
+     * @return 旧存储值
+     */
+    public static byte[] getAndSetBytes(final byte[] key, final byte[] value) {
+        return (byte[]) getTemplate().execute((RedisCallback<byte[]>) connection -> connection.getSet(key, value));
+    }
+
+    /**
+     * 按键值存储数据
+     * @param key   键值
+     * @param value 存储值
+     * @param <T>   值泛型
+     */
+    public static <T extends Serializable> void cmdSet(String key, T value) {
+        setBytes(keySerial(key), strSerial(value));
+    }
+
+    /**
+     * 按键值存储数据
+     * @param key   键值
+     * @param value 存储值
+     * @param <T>   值泛型
+     */
+    public static <T extends Serializable> void set(String key, T value) {
+        setBytes(keySerial(key), objSerial(value));
+    }
+
+    public static <T extends Serializable> Boolean setNx(String key, T value) {
+        return setNxBytes(keySerial(key), objSerial(value));
+    }
+
+    public static Boolean setNxBytes(final byte[] key, final byte[] value) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.setNX(key, value));
+    }
+
+    public static <T extends Serializable> Boolean setNxEx(String key, T value, final int timeout) {
+        return setExNxBytes(keySerial(key), objSerial(value), timeout);
+    }
+
+    public static Boolean setExNxBytes(final byte[] key, final byte[] value, final int timeout) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> {
+            Boolean success = connection.setNX(key, value);
+            if (success != null && success) {
+                connection.expire(key, timeout);
+            }
+            return success;
+        });
+    }
+
+    /**
+     * 按键值存储数据
+     * @param key   键值
+     * @param value 存储值
+     */
+    public static void setBytes(final byte[] key, final byte[] value) {
+        getTemplate().execute((RedisCallback<Void>) connection -> {
+            connection.set(key, value);
+            return null;
+        });
+    }
+
+    /**
+     * 存储一定时间内过期的值
+     * @param key     键值
+     * @param value   存储值
+     * @param timeout 超时时长
+     * @param <T>     存储值泛型
+     */
+    public static <T extends Serializable> void cmdSetEx(final String key, final T value, final int timeout) {
+        setExBytes(keySerial(key), strSerial(value), timeout);
+    }
+
+    /**
+     * 存储一定时间内过期的值
+     * @param key     键值
+     * @param value   存储值
+     * @param timeout 超时时长
+     * @param <T>     存储值泛型
+     */
+    public static <T extends Serializable> void setEx(final String key, final T value, final int timeout) {
+        setExBytes(keySerial(key), objSerial(value), timeout);
+    }
+
+    /**
+     * 存储一定时间内过期的值
+     * @param key     键值
+     * @param value   存储值
+     * @param timeout 超时时长
+     */
+    public static void setExBytes(final byte[] key, final byte[] value, final int timeout) {
+        getTemplate().execute((RedisCallback<Void>) connection -> {
+            connection.setEx(key, timeout, value);
+            return null;
+        });
+    }
+
+    /**
+     * 为指定键值增加过期时长
+     * @param key     键值
+     * @param timeout 超时时长
+     */
+    public static boolean expire(final String key, final int timeout) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.expire(keySerial(key), timeout));
+    }
+
+    /**
+     * 为指定键值增加有效期(日期时间)
+     * @param key  键值
+     * @param date 有效期
+     */
+    public static boolean expireAt(final String key, final long date) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.expireAt(keySerial(key), date));
+    }
+
+    /**
+     * 判断键值还有多少秒超时
+     * @param key 键值
+     */
+    public static long ttl(final String key) {
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.ttl(keySerial(key)));
+    }
+
+    /**
+     * 判断指定键值是否存在
+     * @param key 键值
+     * @return 布尔值
+     */
+    public static boolean exists(final String key) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.exists(keySerial(key)));
+    }
+
+    /**
+     * 删除指定键值数据
+     * @param keys 键值数组
+     * @return 删除的个数
+     */
+    public static long del(final String... keys) {
+        if (keys == null || keys.length == 0) {
+            return 0;
+        }
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.del(fields(keys)));
+    }
+
+    /**
+     * 获取指定的hash存储的值
+     * @param key   键值
+     * @param field hash键值
+     * @param <T>   存储值泛型
+     * @return 存储值
+     */
+    public static <T> T cmdHGet(String key, Class<T> cl, String field) {
+        return strDeserial(cl, hGetBytes(keySerial(key), keySerial(field)));
+    }
+
+    /**
+     * 获取指定的hash存储的值
+     * @param key   键值
+     * @param field hash键值
+     * @param <T>   存储值泛型
+     * @return 存储值
+     */
+    public static <T> T hGet(String key, String field) {
+        return objDeserial(hGetBytes(keySerial(key), keySerial(field)));
+    }
+
+    /**
+     * 获取指定的hash存储的值
+     * @param key   键值
+     * @param field hash键值
+     * @return 存储值
+     */
+    public static byte[] hGetBytes(final byte[] key, final byte[] field) {
+        return (byte[]) getTemplate().execute((RedisCallback<byte[]>) connection -> connection.hGet(key, field));
+    }
+
+
+    /**
+     * 获取多个hash存储的值,由于值类型不同,不做反序列化
+     * @param key    键值
+     * @param fields 多个hash键值
+     * @return 存储值
+     */
+    public static List<byte[]> hMGet(final String key, final String... fields) {
+        return (List<byte[]>) getTemplate().execute((RedisCallback<List<byte[]>>) connection -> connection.hMGet(keySerial(key), fields(fields)));
+    }
+
+    /**
+     * 获取多个hash存储的值,值类型相同时使用
+     * @param key    键值
+     * @param fields 多个hash键值
+     * @param <T>    存储值泛型
+     * @return 存储值
+     */
+    public static <T> List<T> hMGetSameType(Class<T> cl, String key, String... fields) {
+        return strListDeserial(cl, hMGet(key, fields));
+    }
+
+    /**
+     * 获取多个hash存储的值,值类型相同时使用
+     * @param key    键值
+     * @param fields 多个hash键值
+     * @param <T>    存储值泛型
+     * @return 存储值
+     */
+    public static <T> List<T> hMGetSameType(String key, String... fields) {
+        return objListDeserial(hMGet(key, fields));
+    }
+
+    /**
+     * 判断指定的hash存储是否存在
+     * @param key   键值
+     * @param field hash键值
+     * @return 布尔值
+     */
+    public static Boolean hExists(final String key, final String field) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.hExists(keySerial(key), keySerial(field)));
+    }
+
+    /**
+     * 获取整个hash存储,由于值类型不同,不做反序列化
+     * @param key 键值
+     * @return 存储的数据映射
+     */
+    public static List<String> hKeys(final String key) {
+        return (List<String>) getTemplate().execute((RedisCallback<List<String>>) connection -> strListDeserial(String.class, connection.hKeys(keySerial(key))));
+    }
+
+    /**
+     * 获取整个hash存储,由于值类型不同,不做反序列化
+     * @param key 键值
+     * @return 存储的数据映射
+     */
+    public static Map<byte[], byte[]> hGetAllBytes(final String key) {
+        return (Map<byte[], byte[]>) getTemplate().execute((RedisCallback<Map<byte[], byte[]>>) connection -> connection.hGetAll(keySerial(key)));
+    }
+
+    /**
+     * 获取整个hash存储,由于值类型不同,不做反序列化
+     * @param key 键值
+     * @return 存储的数据映射
+     */
+    public static Map<String, byte[]> hGetAll(String key) {
+        return mapKeyDeserial(hGetAllBytes(key));
+    }
+
+    /**
+     * 获取整个hash存储,值类型相同时使用
+     *
+     * @param key 键值
+     * @param <T> hash存储值泛型
+     * @return 存储的数据映射
+     */
+    public static <T> Map<String, T> hGetAllSameType(String key, Class<T> cl) {
+        return strMapDeserial(cl, hGetAllBytes(key));
+    }
+
+    /**
+     * 获取整个hash存储,值类型相同时使用
+     * @param key 键值
+     * @param <T> hash存储值泛型
+     * @return 存储的数据映射
+     */
+    public static <T> Map<String, T> hGetAllSameType(final String key) {
+        return objMapDeserial(hGetAllBytes(key));
+    }
+
+    /**
+     * 存储单个hash值
+     * @param key   键值
+     * @param field hash存储
+     * @param value 存储值
+     * @param <T>   存储值泛型
+     */
+    public static <T extends Serializable> void cmdHSet(final String key, final String field, final T value) {
+        hSetBytes(key, field, strSerial(value));
+    }
+
+    /**
+     * 存储单个hash值
+     * @param key   键值
+     * @param field hash存储
+     * @param value 存储值
+     * @param <T>   存储值泛型
+     */
+    public static <T extends Serializable> void hSet(final String key, final String field, final T value) {
+        hSetBytes(key, field, objSerial(value));
+    }
+
+    /**
+     * 存储单个hash值
+     * @param key   键值
+     * @param field hash存储
+     * @param value 存储值
+     */
+    public static void hSetBytes(final String key, final String field, final byte[] value) {
+        getTemplate().execute((RedisCallback<Boolean>) connection -> connection.hSet(keySerial(key), keySerial(field), value));
+    }
+
+    /**
+     * hash存储整个map
+     * @param key 键值
+     * @param map 存储map
+     * @param <T> 存储值泛型
+     */
+    public static <T extends Serializable> void cmdHMSet(final String key, final Map<String, T> map) {
+        hMSetBytes(key, strMapSerial(map));
+    }
+
+    /**
+     * hash存储整个map
+     * @param key 键值
+     * @param map 存储map
+     * @param <T> 存储值泛型
+     */
+    public static <T extends Serializable> void hMSet(final String key, final Map<String, T> map) {
+        hMSetBytes(key, objMapSerial(map));
+    }
+
+    /**
+     * hash存储整个map
+     * @param key 键值
+     * @param map 存储map
+     */
+    public static void hMSetBytes(final String key, final Map<byte[], byte[]> map) {
+        getTemplate().execute((RedisCallback<Object>) connection -> {
+            connection.hMSet(keySerial(key), map);
+            return null;
+        });
+    }
+
+    /**
+     * 删除指定hash数据
+     * @param key    键值
+     * @param fields hash键值
+     * @return 删除的数量
+     */
+    public static long hDel(final String key, final String... fields) {
+        if (StringUtils.isEmpty(key) || fields == null || fields.length == 0) {
+            return 0;
+        }
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.hDel(keySerial(key), fields(fields)));
+    }
+
+    /**
+     * set获取所有成员
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> List<T> sMemebers(String key, Class<T> cl) {
+        return strListDeserial(cl, sMemebersBytesList(key));
+    }
+
+    /**
+     * set获取所有成员
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> List<T> sMemebers(String key) {
+        return objListDeserial(sMemebersBytesList(key));
+    }
+
+    /**
+     * set获取所有成员
+     * @param key 键值
+     * @return 值列表
+     */
+    public static Set<byte[]> sMemebersBytesList(final String key) {
+        return (Set<byte[]>) getTemplate().execute((RedisCallback<Set<byte[]>>) connection -> connection.sMembers(keySerial(key)));
+    }
+
+    /**
+     * 判断是否为set成员
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> boolean cmdSIsMemeber(String key, T value) {
+        return sIsMemeber(keySerial(key), strSerial(value));
+    }
+
+    /**
+     * 判断是否为set成员
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> boolean sIsMemeber(String key, T value) {
+        return sIsMemeber(keySerial(key), objSerial(value));
+    }
+
+    /**
+     * 判断是否为set成员
+     *
+     * @param key 键值
+     * @return 值列表
+     */
+    public static boolean sIsMemeber(final byte[] key, final byte[] value) {
+        return (Boolean) getTemplate().execute(new RedisCallback<Boolean>() {
+            @Override
+            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
+                return connection.sIsMember(key, value);
+            }
+        });
+    }
+
+    /**
+     * 判断是否为set成员
+     *
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> boolean cmdSContainMemeber(final String key, final T... values) {
+        if (values == null || values.length == 0) {
+            return false;
+        }
+        return sContainMemeber(keySerial(key), strArraySerial(values));
+    }
+
+    /**
+     * 判断是否为set成员
+     *
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> boolean sContainMemeber(final String key, final T... values) {
+        if (values == null || values.length == 0) {
+            return false;
+        }
+        return sContainMemeber(keySerial(key), objArraySerial(values));
+    }
+
+    /**
+     * 判断是否为set成员
+     *
+     * @param key 键值
+     * @return 值列表
+     */
+    public static boolean sContainMemeber(byte[] key, byte[][] values) {
+        String sContainMemeberScript = "local vs = ARGV[1] for i=1,#vs,1 do if (redis.call('SISMEMBER',KEY[1], vs[i]) == 1) then return true end end return false";
+        ArrayList<byte[]> keys = new ArrayList<>();
+        keys.add(key);
+        Object[] objValues = values;
+        return (Boolean) getTemplate().execute(new DefaultRedisScript<>(sContainMemeberScript, Boolean.class), keys, objValues);
+    }
+
+    /**
+     * set添加
+     *
+     * @param key    键值
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long cmdSAdd(final String key, final T... values) {
+        return sAdd(keySerial(key), strArraySerial(values));
+    }
+
+    /**
+     * set添加
+     *
+     * @param key    键值
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long sAdd(final String key, final T... values) {
+        return sAdd(keySerial(key), objArraySerial(values));
+    }
+
+    /**
+     * set添加
+     *
+     * @param key    键值
+     * @param values 值数组
+     */
+    public static long sAdd(final byte[] key, final byte[][] values) {
+        return (long) getTemplate().execute(new RedisCallback<Long>() {
+
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                return connection.sAdd(key, values);
+            }
+        });
+    }
+
+    /**
+     * set添加所有的
+     *
+     * @param keys   键值组
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long cmdSAddAll(final List<String> keys, final List<T[]> values) {
+        return (long) getTemplate().execute(new RedisCallback<Long>() {
+
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                long count = 0;
+                for (int i = 0; i < keys.size(); i++) {
+                    connection.sAdd(keySerial(keys.get(i)), strArraySerial(values.get(i)));
+                }
+                return count;
+            }
+        });
+    }
+
+    /**
+     * set添加所有的
+     *
+     * @param keys
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long sAddAll(final List<String> keys, final List<T[]> values) {
+        return (long) getTemplate().execute(new RedisCallback<Long>() {
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                long count = 0;
+                for (int i = 0; i < keys.size(); i++) {
+                    connection.sAdd(keySerial(keys.get(i)), objArraySerial(values.get(i)));
+                }
+                return count;
+            }
+        });
+    }
+
+    /**
+     * set删除
+     *
+     * @param key    键值
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long cmdSRem(final String key, final T... values) {
+        return sRem(keySerial(key), strArraySerial(values));
+    }
+
+    /**
+     * set删除
+     *
+     * @param key    键值
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long sRem(final String key, final T... values) {
+        return sRem(keySerial(key), objArraySerial(values));
+    }
+
+    /**
+     * set删除
+     *
+     * @param key    键值
+     * @param values 值数组
+     */
+    public static long sRem(final byte[] key, final byte[]... values) {
+        return (Long) getTemplate().execute(new RedisCallback<Long>() {
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                return connection.sRem(key, values);
+            }
+        });
+    }
+
+    /**
+     * 事务模式执行redis操作
+     *
+     * @param callback 回调方法
+     * @return 返回结果
+     */
+    public static <T> T execute(SessionCallback<T> callback) {
+        return (T) getTemplate().execute(callback);
+    }
+
+
+    /**
+     * 键值序列化
+     *
+     * @param key 键值
+     * @return 结果字节数组
+     */
+    public static byte[] keySerial(String key) {
+        return getKeySerializer().serialize(key);
+    }
+
+    /**
+     * 键值反序列化
+     *
+     * @param key 键值字节数组
+     * @return 键值
+     */
+    public static String keyDeserial(byte[] key) {
+        return (String) getKeySerializer().deserialize(key);
+    }
+
+    /**
+     * 多个键值序列化
+     *
+     * @param fields 键值数据
+     * @return 多个键值的字节矩阵
+     */
+    public static byte[][] fields(String... fields) {
+        RedisSerializer<String> s = getKeySerializer();
+        int len = fields.length;
+        byte[][] fs = new byte[len][];
+        for (int i = 0; i < fields.length; i++) {
+            fs[i] = s.serialize(fields[i]);
+        }
+        return fs;
+    }
+
+    /**
+     * 取得值redis序列化对象
+     *
+     * @return redis序列化对象
+     */
+    public static <T> RedisSerializer<T> getValueSerializer(Class<T> type) {
+        return new GenericToStringSerializer<>(type);
+    }
+
+    /**
+     * 取得值redis序列化对象
+     *
+     * @return redis序列化对象
+     */
+    public static <T> RedisSerializer<T> getValueSerializer() {
+        return getTemplate().getValueSerializer();
+    }
+
+    /**
+     * 取得键redis序列化对象
+     *
+     * @return redis序列化对象
+     */
+    public static <T> RedisSerializer<T> getKeySerializer() {
+        return getTemplate().getStringSerializer();
+    }
+
+    /**
+     * 存储值的序列化
+     *
+     * @param v   值对象
+     * @param <T> 值泛型
+     * @return 结果字节数组
+     */
+    public static <T> byte[] strSerial(T v) {
+        Class<T> c = (Class<T>) v.getClass();
+        return getValueSerializer(c).serialize(v);
+    }
+
+    /**
+     * 存储值的序列化
+     *
+     * @param v   值对象
+     * @param <T> 值泛型
+     * @return 结果字节数组
+     */
+    public static <T> byte[] objSerial(T v) {
+        return getValueSerializer().serialize(v);
+    }
+
+    /**
+     * 存储值的反序列化
+     *
+     * @param cl  结果对象类型
+     * @param v   值字节数组
+     * @param <T> 值泛型
+     * @return 值对象
+     */
+    public static <T> T strDeserial(Class<T> cl, byte[] v) {
+        return getValueSerializer(cl).deserialize(v);
+    }
+
+    /**
+     * 存储值的反序列化
+     *
+     * @param v   值字节数组
+     * @param <T> 值泛型
+     * @return 值对象
+     */
+    public static <T> T objDeserial(byte[] v) {
+        return (T) getValueSerializer().deserialize(v);
+    }
+
+    /**
+     * 反序列化值列表
+     *
+     * @param bs  存储值的字节数组列表
+     * @param <T> 值泛型
+     * @return 值对象列表
+     */
+    public static <T> List<T> strListDeserial(Class<T> cl, Collection<byte[]> bs) {
+        List<T> l = new ArrayList<>();
+        RedisSerializer<T> ser = getValueSerializer(cl);
+        for (byte[] b : bs) {
+            l.add(ser.deserialize(b));
+        }
+        return l;
+    }
+
+    /**
+     * 反序列化值列表
+     *
+     * @param bs  存储值的字节数组列表
+     * @param <T> 值泛型
+     * @return 值对象列表
+     */
+    public static <T> List<T> objListDeserial(Collection<byte[]> bs) {
+        List<T> l = new ArrayList<>();
+        RedisSerializer<T> ser = getValueSerializer();
+        for (byte[] b : bs) {
+            l.add(ser.deserialize(b));
+        }
+        return l;
+    }
+
+    /**
+     * 序列化map数据
+     *
+     * @param m   map对象
+     * @param <T> map中值的泛型
+     * @return 字节到字节映射
+     */
+    public static <T> Map<byte[], byte[]> strMapSerial(Map<String, T> m) {
+        Map<byte[], byte[]> bs = new HashMap<>(16);
+        RedisSerializer<T> serializer = null;
+        for (Map.Entry<String, T> e : m.entrySet()) {
+            T v = e.getValue();
+            if (v == null) {
+                continue;
+            }
+            if (serializer == null) {
+                serializer = getValueSerializer((Class<T>) v.getClass());
+            }
+            bs.put(keySerial(e.getKey()), serializer.serialize(v));
+        }
+        return bs;
+    }
+
+    /**
+     * 序列化map数据
+     *
+     * @param m   map对象
+     * @param <T> map中值的泛型
+     * @return 字节到字节映射
+     */
+    public static <T> Map<byte[], byte[]> objMapSerial(Map<String, T> m) {
+        Map<byte[], byte[]> bs = new HashMap<>(16);
+        RedisSerializer<T> serializer = getValueSerializer();
+        for (Map.Entry<String, T> e : m.entrySet()) {
+            T v = e.getValue();
+            if (v == null) {
+                continue;
+            }
+            bs.put(keySerial(e.getKey()), serializer.serialize(v));
+        }
+        return bs;
+    }
+
+    /**
+     * 反序列化字节到字节映射的键值
+     *
+     * @param bs 字节到字节映射
+     * @return 字符串到字节数据的映射
+     */
+    public static Map<String, byte[]> mapKeyDeserial(Map<byte[], byte[]> bs) {
+        Map<String, byte[]> m = new HashMap<>(16);
+        for (Map.Entry<byte[], byte[]> e : bs.entrySet()) {
+            m.put(keyDeserial(e.getKey()), e.getValue());
+        }
+        return m;
+    }
+
+    /**
+     * 反序列化字节到字节映射数据
+     *
+     * @param bs  字节到字节映射
+     * @param <T> map值泛型
+     * @return map数据对象
+     */
+    public static <T> Map<String, T> strMapDeserial(Class<T> cl, Map<byte[], byte[]> bs) {
+        Map<String, T> m = new HashMap<>(16);
+        RedisSerializer<T> ser = getValueSerializer(cl);
+        for (Map.Entry<byte[], byte[]> e : bs.entrySet()) {
+            m.put(keyDeserial(e.getKey()), ser.deserialize(e.getValue()));
+        }
+        return m;
+    }
+
+    /**
+     * 反序列化字节到字节映射数据
+     *
+     * @param bs  字节到字节映射
+     * @param <T> map值泛型
+     * @return map数据对象
+     */
+    public static <T> Map<String, T> objMapDeserial(Map<byte[], byte[]> bs) {
+        Map<String, T> m = new HashMap<>(16);
+        RedisSerializer<T> ser = getValueSerializer();
+        for (Map.Entry<byte[], byte[]> e : bs.entrySet()) {
+            m.put(keyDeserial(e.getKey()), ser.deserialize(e.getValue()));
+        }
+        return m;
+    }
+
+    /**
+     * 序列化数组
+     *
+     * @param vs  值数组
+     * @param <T> 值泛型
+     * @return 字节二维数组
+     */
+    public static <T> byte[][] strArraySerial(T... vs) {
+        byte[][] bss = new byte[vs.length][];
+        RedisSerializer<T> ser = null;
+        for (int i = 0; i < vs.length; i++) {
+            T v = vs[i];
+            if (v == null) {
+                continue;
+            }
+            if (ser == null) {
+                ser = getValueSerializer((Class<T>) v.getClass());
+            }
+            bss[i] = ser.serialize(v);
+        }
+        return bss;
+    }
+
+    /**
+     * 序列化数组
+     *
+     * @param vs  值数组
+     * @param <T> 值泛型
+     * @return 字节二维数组
+     */
+    public static <T> byte[][] objArraySerial(T... vs) {
+        byte[][] bss = new byte[vs.length][];
+        RedisSerializer<T> ser = getValueSerializer();
+        for (int i = 0; i < vs.length; i++) {
+            T v = vs[i];
+            if (v == null) {
+                continue;
+            }
+            bss[i] = ser.serialize(v);
+        }
+        return bss;
+    }
+
+}

+ 153 - 0
apm-common/src/main/java/com/persagy/apm/common/helper/SpringHelper.java

@@ -0,0 +1,153 @@
+package com.persagy.apm.common.helper;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.persagy.apm.common.utils.TypeConvertUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.ApplicationContext;
+
+import java.util.Map;
+
+/**
+ * Spring助手
+ * @author Charlie Yu
+ * @version 1.0 2021-03-02
+ */
+public class SpringHelper {
+
+    /**
+     * spring上下文
+     * @return spring上下文
+     */
+    public static ApplicationContext getContext(){
+        return SpringUtil.getApplicationContext();
+    }
+
+    /**
+     * 根据类名搜索相应的bean对象
+     * @param <T> 类模板标识返回值类型
+     * @param clazz 模板类
+     * @return bean对象
+     */
+    public static <T>T getBean(Class<T> clazz){
+        return SpringUtil.getBean(clazz);
+    }
+
+    /**
+     * 根据类型取得所有
+     * @param type 接口类
+     * @return 取得所有的实现bean
+     */
+    public static <T> Map<String, T> getBeansOfType(Class<T> type){
+        return SpringUtil.getBeansOfType(type);
+    }
+
+    /**
+     * 根据bean名称和bean类型搜索相应的bean对象
+     * @param <T> 类模板标识返回值类型
+     * @param beanName bean名称
+     * @param clazz 模板类
+     * @return bean对象
+     */
+    public static <T>T getBean(String beanName,Class<T> clazz){
+        return SpringUtil.getBean(beanName, clazz);
+    }
+
+    /**
+     * 根据bean名称搜索相应的bean对象
+     * @param beanName bean名称
+     * @return bean对象
+     */
+    public static <T>T getBean(String beanName){
+        return SpringUtil.getBean(beanName);
+    }
+
+    /**
+     * 根据配置名称取得字符串配置值(配置名形式为nodeName.nodeName)
+     * @param key 配置名称
+     * @return 指定配置字符串值
+     */
+    public static String getString(String key) {
+        return SpringUtil.getProperty(key);
+    }
+
+    /**
+     * 根据配置名称取得字符串配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 指定默认值
+     * @return 最终配置值
+     */
+    public static String getString(String key, String defaultValue) {
+        String value = getString(key);
+        return StringUtils.isBlank(value) ? defaultValue : value;
+    }
+
+    /**
+     * 根据配置名称取得整型配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认整型值
+     * @return 最终配置值
+     */
+    public static int getInt(String key, int defaultValue) {
+        String value = getString(key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseInt(value);
+    }
+    /**
+     * 根据配置名称取得布尔配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认布尔值
+     * @return 最终配置值
+     */
+    public static boolean getBoolean(String key, boolean defaultValue) {
+        String value = getString(key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseBoolean(value);
+    }
+
+    /**
+     * 根据配置名称取得长整型配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认长整型值
+     * @return 最终配置值
+     */
+    public static long getLong(String key, long defaultValue) {
+        String value = getString(key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseLong(value);
+    }
+
+    /**
+     * 根据配置名称取得浮点型配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认浮点型值
+     * @return 最终配置值
+     */
+    public static float getFloat(String key, float defaultValue) {
+        String value = getString(key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseFloat(value);
+    }
+
+    /**
+     * 根据配置名称取得双浮点型配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认双浮点型值
+     * @return 最终配置值
+     */
+    public static double getDouble(String key, double defaultValue) {
+        String value = getString(key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseDouble(value);
+    }
+
+}

+ 615 - 0
apm-common/src/main/java/com/persagy/apm/common/lang/PsDate.java

@@ -0,0 +1,615 @@
+package com.persagy.apm.common.lang;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+
+
+/**
+ * 自定日期类,用于转化表示日期串,日期对象等
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+public class PsDate implements Serializable, Comparable<PsDate>, Cloneable {
+
+    /** 序列id */
+    private static final long serialVersionUID = 5668159214884319312L;
+
+    /** 默认的日期格式化对象 */
+    public static DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
+
+    /** 默认时区 */
+    public static ZoneId DEFAULT_ZONE_ID = ZoneId.of(ZoneId.SHORT_IDS.get("CTT"));
+
+    /*日期时间常量*/
+    /** 一天毫秒数 */
+    public static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
+    /** 一小时毫秒数 */
+    public static final int MILLIS_PER_HOUR = 60 * 60 * 1000;
+    /** 一分钟毫秒数 */
+    public static final int MILLIS_PER_MINUTE = 60 * 1000;
+    /** 一秒毫秒数 */
+    public static final int MILLIS_PER_SECOND = 1000;
+
+    /** 保存时间的日期对象 */
+    protected Calendar calendar = null;
+
+    /**
+     * 自定义日期无参构造方法。
+     */
+    public PsDate() {
+        calendar = Calendar.getInstance();
+        init();
+    }
+
+    /**
+     * 通过毫秒数来构造自定义日期对象
+     * (以从1970年1月1日0时0分0秒到现在的毫秒数来构造日期)
+     * @param millis 毫秒数
+     */
+    public PsDate(long millis) {
+        this();
+        setMillis(millis);
+    }
+
+    /**
+     * 通过日期对象构造自定义日期对象
+     * @param date 日期对象
+     */
+    public PsDate(Date date) {
+        this();
+        setDate(date);
+    }
+
+    /**
+     * 通过默认形式日期串构造自定义日期对象
+     * @param date 默认格式日期串
+     */
+    public PsDate(String date) {
+        this(date, DEFAULT_FORMAT);
+    }
+
+    /**
+     * 通过默认形式日期串构造自定义日期对象
+     * @param date   默认格式日期串
+     * @param format 格式化串
+     */
+    public PsDate(String date, String format) {
+        this();
+        DateTimeFormatter dateFormat = null;
+        if (format != null) {
+            dateFormat = DateTimeFormatter.ofPattern(format);
+        }
+        parse(date, dateFormat);
+    }
+
+    /**
+     * 通过日期串和指定的格式化对象构造自定义日期对象
+     * @param date       符合格式化对象的日期时间对象
+     * @param dateFormat 格式化对象
+     */
+    public PsDate(String date, DateTimeFormatter dateFormat) {
+        this();
+        parse(date, dateFormat);
+    }
+
+    /**
+     * 按指定日期形式解析日期字符串
+     * @param date       日期字符串
+     * @param dateFormat 日期形式
+     */
+    protected void parse(String date, DateTimeFormatter dateFormat) {
+        if (StringUtils.isEmpty(date)) {
+            return;
+        }
+        if (dateFormat == null) {
+            dateFormat = DEFAULT_FORMAT;
+        }
+        LocalDate localDate = LocalDate.parse(date, dateFormat);
+        setDate(Date.from(localDate.atStartOfDay().toInstant(ZoneOffset.UTC)));
+    }
+
+    /**
+     * 设置指定时间属性的值
+     * @param field 时间属性
+     * @param value 时间属性的值
+     */
+    public void set(int field, int value) {
+        calendar.set(field, value);
+    }
+
+    /**
+     * 取得定时间属性的值
+     * @param field 时间属性
+     * @return 定时间属性的值
+     */
+    public int get(int field) {
+        return calendar.get(field);
+    }
+
+    /**
+     * 初始方法
+     */
+    protected void init() {
+        set(Calendar.HOUR_OF_DAY, 0);
+        set(Calendar.MINUTE, 0);
+        set(Calendar.SECOND, 0);
+        set(Calendar.MILLISECOND, 0);
+    }
+
+    /**
+     * 根据对象构建自定义日期对象
+     * @param obj 对象
+     * @return 自定义日期对象
+     */
+    public static PsDate parsePsDate(Object obj) {
+        if (obj == null || "".equals(obj.toString().trim())) {
+            return null;
+        }
+        if (obj instanceof PsDate) {
+            return new PsDate(((PsDate) obj).getMillis());
+        }
+
+        if (obj instanceof Date) {
+            return new PsDate((Date) obj);
+        }
+        if (obj instanceof Integer) {
+            return new PsDate(((Number) obj).longValue() * 1000);
+        }
+        if (obj instanceof Number) {
+            return new PsDate(((Number) obj).longValue());
+        }
+        // 目前存储的格式,都是数字,兼容时间戳会误判,暂不这样处理了
+//        if (NumberUtils.isCreatable(obj.toString())) {
+//            return new PsDate(NumberUtils.toLong(obj.toString()));
+//        }
+        return new PsDate(obj.toString());
+
+    }
+
+    /**
+     * 设置日期对象的时间
+     * @param date 日期对象
+     */
+    public void setDate(Date date) {
+        calendar.setTime(date);
+        init();
+    }
+
+    /**
+     * 取得当前日期所表示的java日期对象
+     * @return java日期对象
+     */
+    public Date getDate() {
+        return calendar.getTime();
+    }
+
+    /**
+     * 设置日期对象的时间
+     * @param millis 日期对象
+     */
+    public void setMillis(long millis) {
+        calendar.setTimeInMillis(millis);
+        init();
+    }
+
+    /**
+     * 取得日期对象的毫秒数
+     * @return 毫秒数
+     */
+    public long getMillis() {
+        return calendar.getTimeInMillis();
+    }
+
+    /**
+     * 比较日期先后,对象日期在参数日期之后为true
+     * @param when 比较的日期的对象
+     * @return 布尔值
+     */
+    public boolean after(PsDate when) {
+        return compareTo(when) > 0;
+    }
+
+    /**
+     * 比较日期先后,对象日期在参数日期之前为true
+     * @param when 比较的日期的对象
+     * @return 布尔值
+     */
+    public boolean before(PsDate when) {
+        return compareTo(when) < 0;
+    }
+
+    /**
+     * 返回指定天数之后的自定义日期对象
+     * @param days 指定的天数
+     * @return 自定义日期对象
+     */
+    public PsDate getDateAfter(int days) {
+        long millis = getMillis() + MILLIS_PER_DAY * days;
+        return new PsDate(millis);
+    }
+
+    /**
+     * 返回指定天数之前的自定义日期对象
+     * @param days 指定的天数
+     * @return 自定义日期对象
+     */
+    public PsDate getDateBefore(int days) {
+        return getDateAfter(-days);
+    }
+
+    /**
+     * 取得当前日期对象在指定的日期对象之后多少天
+     * @param when 要比较的自定义对象
+     * @return 在比较的自定义对象之后多少天
+     */
+    public int getDaysAfter(PsDate when) {
+        return getDaysBetween(when, this);
+    }
+
+    /**
+     * 取得开始日期到结束日期直接相差多少天
+     * @param begin 开始日期
+     * @param end   结束日期
+     * @return 相差天数
+     */
+    public static int getDaysBetween(PsDate begin, PsDate end) {
+        int days = 0;
+        if (begin != null && end != null) {
+            PsDate e = new PsDate(end.getMillis());
+            PsDate b = new PsDate(begin.getMillis());
+            days = (int) (e.getMillis() / MILLIS_PER_DAY - b.getMillis() / MILLIS_PER_DAY);
+        }
+        return days;
+    }
+
+    /**
+     * 格式化时间数字串为两位
+     * @param number 数字
+     * @return 数字串
+     */
+    public String formatNumber(int number) {
+        return number < 10 ? "0" + number : Integer.toString(number);
+    }
+
+    /**
+     * 取得日期对象中的日
+     * @return 天数
+     */
+    public int getDay() {
+        return get(Calendar.DATE);
+    }
+
+    /**
+     * 取得日期对象中的日串
+     * @return 天数串(两位)
+     */
+    public String getDAY() {
+        return formatNumber(getDay());
+    }
+
+    /**
+     * 取得日期对象的月份
+     * @return
+     */
+    public int getMonth() {
+        return get(Calendar.MONTH);
+    }
+
+    /**
+     * 取得日期对象的月份串(两位)
+     * @return 月份串(两位)
+     */
+    public String getMONTH() {
+        return formatNumber(getMonth() + 1);
+    }
+
+    /**
+     * 取得当前日期对象所在月的英文写法
+     * @return 月的英文串
+     */
+    public String getEnMonth() {
+        switch (getMonth()) {
+            case 1:
+                return "Jan";
+            case 2:
+                return "Feb";
+            case 3:
+                return "Mar";
+            case 4:
+                return "Apr";
+            case 5:
+                return "May";
+            case 6:
+                return "Jun";
+            case 7:
+                return "Jul";
+            case 8:
+                return "Aug";
+            case 9:
+                return "Sep";
+            case 10:
+                return "Oct";
+            case 11:
+                return "Nov";
+            case 12:
+                return "Dec";
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 取得日期对象的年份
+     * @return 年份串
+     */
+    public int getYear() {
+        return get(Calendar.YEAR);
+    }
+
+    /**
+     * 取得年字符串
+     * @return 年字符串
+     */
+    public String getYEAR() {
+        return Integer.toString(getYear());
+    }
+
+
+    /**
+     * 取得当前日期对象的星期表示
+     * @return 星期表示
+     */
+    public int getWeek() {
+        return get(Calendar.DAY_OF_WEEK);
+    }
+
+    /**
+     * 取得当前日期对象的星期中文简单表示
+     * @return 星期中文简单表示
+     */
+    public String getSimpleZhWeek() {
+        switch (getWeek()) {
+            case 1:
+                return "日";
+            case 2:
+                return "一";
+            case 3:
+                return "二";
+            case 4:
+                return "三";
+            case 5:
+                return "四";
+            case 6:
+                return "五";
+            case 7:
+                return "六";
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 取得当前日期对象的星期英文表示
+     * @return 星期英文表示
+     */
+    public String getZhWeek() {
+        switch (getWeek()) {
+            case 1:
+                return "星期日";
+            case 2:
+                return "星期一";
+            case 3:
+                return "星期二";
+            case 4:
+                return "星期三";
+            case 5:
+                return "星期四";
+            case 6:
+                return "星期五";
+            case 7:
+                return "星期六";
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 取得当前日期对象的星期英文表示
+     * @return 星期英文表示
+     */
+    public String getEnWeek() {
+        switch (getWeek()) {
+            case 1:
+                return "Sunday";
+            case 2:
+                return "Monday";
+            case 3:
+                return "Tuesday";
+            case 4:
+                return "Wednesday";
+            case 5:
+                return "Thursday";
+            case 6:
+                return "Friday";
+            case 7:
+                return "Saturday";
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 取得当前日期对象的星期英文简单表示
+     * @return 星期英文简单表示
+     */
+    public String getSimpleEnWeek() {
+        switch (getWeek()) {
+            case 1:
+                return "Sun";
+            case 2:
+                return "Mon";
+            case 3:
+                return "Tue";
+            case 4:
+                return "Wed";
+            case 5:
+                return "Thu";
+            case 6:
+                return "Fri";
+            case 7:
+                return "Sat";
+            default:
+                return null;
+        }
+    }
+
+
+    /**
+     * 判断当前日期所在是否是闰年
+     * @return 布尔值
+     */
+    public boolean isLeapYear() {
+        return isLeapYear(getYear());
+    }
+
+    /**
+     * 判断指定年是否是闰年
+     * @param year  指定的年
+     * @return 布尔值
+     */
+    public static boolean isLeapYear(int year) {
+        return (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
+    }
+
+    /**
+     * 取得当前日期对象所在月有多少天
+     * @return 天数
+     */
+    public int getDaysInMonth() {
+        return getDaysMonth(getYear(), getMonth());
+    }
+
+    /**
+     * 取得指定的年月里一个月有多少天
+     * @param year  指定的年
+     * @param month 指定的月
+     * @return 天数
+     */
+    public static int getDaysMonth(int year, int month) {
+        switch (month) {
+            case 1:
+            case 3:
+            case 5:
+            case 7:
+            case 8:
+            case 10:
+            case 12:
+                return 31;
+            case 2:
+                if (isLeapYear(year)) {
+                    return 29;
+                } else {
+                    return 28;
+                }
+            default:
+                return 30;
+        }
+    }
+
+    /**
+     * 取得当前日期所在年包含的周数
+     * @return 年包含的周数
+     */
+    public int getWeekOfYear() {
+        return get(Calendar.WEEK_OF_YEAR);
+    }
+
+    /**
+     * 用默认的格式化串转化日期
+     * @see Object#toString()
+     */
+    @Override
+    public String toString() {
+        return toString(DEFAULT_FORMAT);
+    }
+
+    /**
+     * 用指定的格式化串,格式化日期对象为日期串
+     * @param format 格式化串
+     * @return 格式化后的日期串
+     */
+    public String toString(String format) {
+        if (format == null) {
+            return toString();
+        }
+        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern(format);
+        return toString(dateFormat);
+    }
+
+    /**
+     * 用指定的格式化对象,格式化日期对象为日期串
+     * @param dateFormat 指定的格式化对象
+     * @return 格式化后的日期串
+     */
+    public String toString(DateTimeFormatter dateFormat) {
+        if (dateFormat == null) {
+            return toString();
+        }
+        return dateFormat.format(getDate().toInstant().atZone(PsDate.DEFAULT_ZONE_ID).toLocalDate());
+    }
+
+    /**
+     * 实现比较接口方法
+     * @see Comparable#compareTo(Object)
+     */
+    @Override
+    public int compareTo(PsDate psDate) {
+        return calendar.compareTo(psDate.calendar);
+    }
+
+    /**
+     * 比较日期是否相等
+     * @param o 比较的对象
+     * @return 布尔值
+     */
+    @Override
+    public boolean equals(Object o) {
+        if ((o != null) && (o instanceof PsDate)) {
+            return this.getMillis() == ((PsDate) o).getMillis();
+        }
+        return false;
+    }
+
+    /**
+     * 克隆日期对象。
+     * @see Object#clone()
+     */
+    @Override
+    public Object clone() {
+        PsDate psDate;
+        try {
+            psDate = (PsDate) super.clone();
+        } catch (CloneNotSupportedException e) {
+            return new PsDate(getMillis());
+        }
+        psDate.calendar = (Calendar) calendar.clone();
+        return psDate;
+    }
+
+    /**
+     * 取得对象的hashCode
+     * @see Date#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return calendar.hashCode();
+    }
+
+}

+ 326 - 0
apm-common/src/main/java/com/persagy/apm/common/lang/PsDateTime.java

@@ -0,0 +1,326 @@
+package com.persagy.apm.common.lang;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+
+
+/**
+ * 自定日期时间类,用于转化表示日期时间串等
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+public class PsDateTime extends PsDate implements Comparable<PsDate> {
+    /** 序列化id */
+    private static final long serialVersionUID = 7280791980952901691L;
+
+    /** 日期格式化对象 */
+    public static DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
+    /** 自定义日期时间无参构造方法。 */
+    public PsDateTime() {
+        super();
+    }
+
+    /**
+     * 通过毫秒数构造自定义日期时间对象
+     * (以从1970年1月1日0时0分0秒到现在的毫秒数来构造日期)
+     * @param millis 毫秒数
+     */
+    public PsDateTime(long millis) {
+        super(millis);
+    }
+
+    /**
+     * 通过日期对象构造自定义日期时间对象
+     * @param date 日期对象
+     */
+    public PsDateTime(Date date) {
+        super(date);
+    }
+
+    /**
+     * 通过自定义日期对象构造自定义日期时间对象
+     * @param date 日期对象
+     */
+    public PsDateTime(PsDate date) {
+        super(date.getMillis());
+    }
+
+    /**
+     * 通过自定义日期对象和时间对象构造自定义日期时间对象
+     * @param date 日期对象
+     */
+    public PsDateTime(PsDate date, PsTime time) {
+        super(date.getMillis() + time.getMillis());
+    }
+
+    /**
+     * 通过默认形式日期串构造自定义日期对象
+     * @param date 默认格式日期串
+     */
+    public PsDateTime(String date) {
+        super(date, DEFAULT_FORMAT);
+    }
+
+    /**
+     * 通过日期串和指定的格式化串构造自定义日期时间对象
+     * @param date   符合格式串的日期时间对象
+     * @param format 日期格式化串
+     */
+    public PsDateTime(String date, String format) {
+        super(date, format);
+    }
+
+    /**
+     * 通过日期串和指定的格式化对象构造自定义日期时间对象
+     * @param date       符合格式化对象的日期时间对象
+     * @param dateFormat 格式化对象
+     */
+    public PsDateTime(String date, DateTimeFormatter dateFormat) {
+        super(date, dateFormat);
+    }
+
+
+    /**
+     * 用日期对象初始化对象
+     */
+    @Override
+    protected void init() {
+        set(Calendar.MILLISECOND, 0);
+    }
+
+    @Override
+    protected void parse(String date, DateTimeFormatter dateFormat) {
+        if (StringUtils.isEmpty(date)) {
+            return;
+        }
+        if (dateFormat == null) {
+            dateFormat = DEFAULT_FORMAT;
+        }
+        LocalDateTime localDate = LocalDateTime.parse(date, dateFormat);
+        ZonedDateTime zdt = localDate.atZone(DEFAULT_ZONE_ID);
+        setDate(Date.from(zdt.toInstant()));
+    }
+
+    /**
+     * 根据对象构建自定义日期时间对象
+     * @param obj 对象
+     * @return 自定义日期时间对象
+     */
+    public static PsDateTime parsePsDateTime(Object obj) {
+        if (obj == null || "".equals(obj.toString())) {
+            return null;
+        }
+        if (obj instanceof PsDate) {
+            return new PsDateTime(((PsDate) obj).getMillis());
+        }
+
+        if (obj instanceof Date) {
+            return new PsDateTime((Date) obj);
+        }
+        if (obj instanceof Integer) {
+            return new PsDateTime(((Integer) obj).longValue() * 1000);
+        }
+        if (obj instanceof Number) {
+            return new PsDateTime(((Number) obj).longValue());
+        }
+//        if (NumberUtils.isCreatable(obj.toString())) {
+//            return new PsDateTime(NumberUtils.toLong(obj.toString()));
+//        }
+        return new PsDateTime(obj.toString());
+
+    }
+
+    /**
+     * 取得自定义日期时间对象中的自定义日期对象
+     * @return 自定义日期对象
+     */
+    public PsDate getDDate() {
+        return new PsDate(getMillis());
+    }
+
+    /**
+     * 取得自定义日期时间对象中的自定义时间对象
+     * @return 自定义时间对象
+     */
+    public PsTime getDTime() {
+        return new PsTime(getMillis());
+    }
+
+    /**
+     * 返回指定天数之后的自定义日期时间对象
+     */
+    @Override
+    public PsDateTime getDateAfter(int days) {
+        return new PsDateTime(getMillis() + MILLIS_PER_DAY * days);
+    }
+
+    /**
+     * 返回指定天数之前的自定义日期时间对象
+     */
+    @Override
+    public PsDateTime getDateBefore(int days) {
+        return getDateAfter(-days);
+    }
+
+    /**
+     * 取得两个日期时间对象相差的小时数
+     * @param begin 开始日期时间
+     * @param end   结束日期时间
+     * @return 小时数
+     */
+    public static int getHoursBetween(PsDateTime begin, PsDateTime end) {
+        return (int) (end.getMillis() / MILLIS_PER_HOUR - begin.getMillis() / MILLIS_PER_HOUR);
+    }
+
+    /**
+     * 取得两个日期时间对象相差的分钟数
+     * @param begin 开始日期时间
+     * @param end   结束日期时间
+     * @return 分钟数
+     */
+    public static int getMinutesBetween(PsDateTime begin, PsDateTime end) {
+        return (int) (end.getMillis() / MILLIS_PER_MINUTE - begin.getMillis() / MILLIS_PER_MINUTE);
+    }
+
+    /**
+     * 取得两个日期时间对象相差的秒数
+     * @param begin 开始日期时间
+     * @param end   结束日期时间
+     * @return 秒数
+     */
+    public static int getSecondsBetween(PsDateTime begin, PsDateTime end) {
+        return (int) (end.getMillis() / MILLIS_PER_SECOND - begin.getMillis() / MILLIS_PER_SECOND);
+    }
+
+    /**
+     * 取得两个日期时间对象相差的毫秒数
+     * @param begin 开始日期时间
+     * @param end   结束日期时间
+     * @return 毫秒数
+     */
+    private static long getMillisBetween(PsDateTime begin, PsDateTime end) {
+        if (begin == null || end == null) {
+            return 0;
+        }
+        return end.getMillis() - begin.getMillis();
+    }
+
+    /**
+     * 取得当前日期时间对象在指定的日期时间对象之后多少毫秒
+     * @param dateTime 要比较的自定义日期时间
+     * @return 当前时间在给定日期时间
+     */
+    public long getMillisAfter(PsDateTime dateTime) {
+        return getMillisBetween(dateTime, this);
+    }
+
+    /**
+     * 取得小时数
+     * @return 小时数
+     */
+    public int getHour() {
+        return get(Calendar.HOUR_OF_DAY);
+    }
+
+    /**
+     * 取得小时数字符串
+     * @return 小时数字符串
+     */
+    public String getHOUR() {
+        return formatNumber(getHour());
+    }
+
+    /**
+     * 取得分钟数
+     * @return 分钟数
+     */
+    public int getMinute() {
+        return get(Calendar.MINUTE);
+    }
+
+    /**
+     * 取得分钟数字符串
+     * @return 分钟数字符串
+     */
+    public String getMINUTE() {
+        return formatNumber(getMinute());
+    }
+
+    /**
+     * 取得秒数
+     * @return 秒数
+     */
+    public int getSecond() {
+        return get(Calendar.SECOND);
+    }
+
+    /**
+     * 取得秒数字符串
+     * @return 秒数字符串
+     */
+    public String getSECOND() {
+        return formatNumber(getSecond());
+    }
+
+    /**
+     * 取得毫秒数
+     * @return 毫秒数
+     */
+    public int getMilliSecond() {
+        return get(Calendar.MILLISECOND);
+    }
+
+    /**
+     * 取得毫秒数串
+     * @return 毫秒数串
+     */
+    public String getMILLISECOND() {
+        int ms = getMilliSecond();
+        return Integer.toString(ms + 1000).substring(1);
+    }
+
+    /**
+     * 实现比较接口方法
+     * @see Comparable#compareTo(Object)
+     */
+    @Override
+    public int compareTo(PsDate psDate) {
+        return calendar.compareTo(psDate.calendar);
+    }
+
+    /**
+     * 比较日期是否相等
+     * @param o 比较的对象
+     * @return 布尔值
+     */
+    @Override
+    public boolean equals(Object o) {
+        if ((o != null) && (o instanceof PsDate)) {
+            return this.getMillis() == ((PsDate) o).getMillis();
+        }
+        return false;
+    }
+
+    /**
+     * 用默认的格式化串转化日期时间
+     * @see Object#toString()
+     */
+    @Override
+    public String toString() {
+        return toString(DEFAULT_FORMAT);
+    }
+
+    @Override
+    public String toString(DateTimeFormatter dateFormat) {
+        if (dateFormat == null) {
+            return toString();
+        }
+        return dateFormat.format(getDate().toInstant().atZone(DEFAULT_ZONE_ID));
+    }
+}

+ 271 - 0
apm-common/src/main/java/com/persagy/apm/common/lang/PsTime.java

@@ -0,0 +1,271 @@
+package com.persagy.apm.common.lang;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+
+
+/**
+ * 自定时间类,用于转化表示时间串等
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+public class PsTime extends PsDate {
+    /** 序列化id */
+    private static final long serialVersionUID = 733417362967334525L;
+
+    /** 时间格式化对象 */
+    public static DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("HHmmss");
+
+    /**
+     * 自定义时间无参构造方法。
+     */
+    public PsTime() {
+        super();
+    }
+
+    /**
+     * 通过毫秒数构造自定义时间对象
+     * (以从1970年1月1日0时0分0秒到现在的毫秒数来构造日期)
+     * @param millis 毫秒数
+     */
+    public PsTime(long millis) {
+        super(millis);
+    }
+
+    /**
+     * 通过日期对象构造自定义时间对象
+     *
+     * @param date 日期对象
+     */
+    public PsTime(Date date) {
+        super(date);
+    }
+
+    /**
+     * 通过默认形式时间串构造自定义时间对象
+     *
+     * @param time 默认格式日期串
+     */
+    public PsTime(String time) {
+        super(time, DEFAULT_FORMAT);
+    }
+
+    /**
+     * 通过时间串和指定的格式化串构造自定义时间对象
+     *
+     * @param time   符合格式串的时间对象
+     * @param format 时间格式化串
+     */
+    public PsTime(String time, String format) {
+        super(time, format);
+    }
+
+    /**
+     * 通过时间串和指定的格式化对象构造自定义时间对象
+     *
+     * @param time       符合格式化对象的时间对象
+     * @param timeFormat 格式化对象
+     */
+    public PsTime(String time, DateTimeFormatter timeFormat) {
+        super(time, timeFormat);
+    }
+
+    /**
+     * 根据对象构建自定义时间对象
+     * @param obj 对象
+     * @return 自定义时间对象
+     */
+    public static PsTime parsePsTime(Object obj) {
+        if (obj == null || "".equals(obj.toString().trim())) {
+            return null;
+        }
+        if (obj instanceof PsDate) {
+            return new PsTime(((PsDate) obj).getMillis());
+        }
+
+        if (obj instanceof Date) {
+            return new PsTime((Date) obj);
+        }
+        if (obj instanceof Integer) {
+            return new PsTime(((Integer) obj).longValue() * 1000);
+        }
+        if (obj instanceof Number) {
+            return new PsTime(((Number) obj).longValue());
+        }
+//        if (NumberUtils.isCreatable(obj.toString())) {
+//            return new PsTime(NumberUtils.toLong(obj.toString()));
+//        }
+        return new PsTime(obj.toString());
+    }
+
+    /**
+     * 用时间对象初始化对象
+     */
+    @Override
+    protected void init() {
+        set(Calendar.YEAR, 1970);
+        set(Calendar.MONTH, 1);
+        set(Calendar.DATE, 1);
+    }
+
+    @Override
+    protected void parse(String date, DateTimeFormatter dateFormat) {
+        if (StringUtils.isEmpty(date)) {
+            return;
+        }
+        if (dateFormat == null) {
+            dateFormat = DEFAULT_FORMAT;
+        }
+        LocalTime localDate = LocalTime.parse(date, dateFormat);
+        ZonedDateTime zdt = localDate.atDate(LocalDate.now()).atZone(PsDate.DEFAULT_ZONE_ID);
+        setDate(Date.from(zdt.toInstant()));
+    }
+
+    /**
+     * 比较时间先后,对象时间在参数时间之后为true
+     * @param when 比较的时间对象
+     * @return 布尔值
+     */
+    public boolean after(PsTime when) {
+        return compareTo(when) > 0;
+    }
+
+    /**
+     * 比较时间先后,对象时间在参数时间之前为true
+     * @param when 比较的时间对象
+     * @return 布尔值
+     */
+    public boolean before(PsTime when) {
+        return compareTo(when) < 0;
+    }
+
+    /**
+     * 比较日期先后,对象日期在参数日期之后为true
+     * @param when 比较的日期的对象
+     * @return 布尔值
+     * @deprecated
+     */
+    @Override
+    public boolean after(PsDate when) {
+        return getMillis() > 0;
+    }
+
+    /**
+     * 比较日期先后,对象日期在参数日期之前为true
+     * @param when 比较的日期的对象
+     * @return 布尔值
+     * @deprecated
+     */
+    @Override
+    public boolean before(PsDate when) {
+        return getMillis() < 0;
+    }
+
+    /**
+     * 返回指定天数之后的自定义日期对象
+     * @param days 指定的天数
+     * @return 自定义日期对象
+     * @deprecated
+     */
+    @Override
+    public PsDate getDateAfter(int days) {
+        return null;
+    }
+
+    /**
+     * 返回指定天数之前的自定义日期对象
+     * @param days 指定的天数
+     * @return 自定义日期对象
+     * @deprecated
+     */
+    @Override
+    public PsDate getDateBefore(int days) {
+        return getDateAfter(-days);
+    }
+
+    /**
+     * 取得当前日期对象在指定的日期对象之后多少天
+     * @param when 要比较的自定义对象
+     * @return 在比较的自定义对象之后多少天
+     * @deprecated
+     */
+    @Override
+    public int getDaysAfter(PsDate when) {
+        return 0;
+    }
+
+    /**
+     * 取得两个时间对象相差的小时数
+     * @param begin 开始时间
+     * @param end   结束时间
+     * @return 相差的小时整数
+     */
+    public static int getHoursBetween(PsTime begin, PsTime end) {
+        return (int) (getMilisBetween(begin, end) / MILLIS_PER_HOUR);
+    }
+
+    /**
+     * 取得两个时间对象相差的分钟数
+     * @param begin 开始时间
+     * @param end   结束时间
+     * @return 相差的分钟整数
+     */
+    public static int getMinutesBetween(PsTime begin, PsTime end) {
+        return (int) (getMilisBetween(begin, end) / MILLIS_PER_MINUTE);
+    }
+
+    /**
+     * 取得两个时间对象相差的秒数
+     * @param begin 开始时间
+     * @param end   结束时间
+     * @return 相差的秒整数
+     */
+    public static int getSecondsBetween(PsTime begin, PsTime end) {
+        return (int) (getMilisBetween(begin, end) / MILLIS_PER_SECOND);
+    }
+
+    /**
+     * 取得两个时间对象相差的毫秒数
+     * @param begin 开始时间
+     * @param end   结束时间
+     * @return 相差的毫秒整数
+     */
+    public static long getMilisBetween(PsTime begin, PsTime end) {
+        if (begin == null || end == null) {
+            return 0;
+        }
+        return end.getMillis() - begin.getMillis();
+    }
+
+    /**
+     * 取得当前时间对象在指定的时间对象之后多少毫秒
+     * @param time 要比较的自定义时间
+     * @return 当前时间在给定时间
+     */
+    public long getMillisAfter(PsTime time) {
+        return getMilisBetween(time, this);
+    }
+
+    /**
+     * 用默认的格式化串转化日期
+     * @see Object#toString()
+     */
+    @Override
+    public String toString() {
+        return toString(DEFAULT_FORMAT);
+    }
+
+    @Override
+    public String toString(DateTimeFormatter dateFormat) {
+        if (dateFormat == null) {
+            return toString();
+        }
+        return dateFormat.format(getDate().toInstant().atZone(PsDate.DEFAULT_ZONE_ID).toLocalTime());
+    }
+}

+ 19 - 0
apm-common/src/main/java/com/persagy/apm/common/model/annotation/SwaggerDisplayEnum.java

@@ -0,0 +1,19 @@
+package com.persagy.apm.common.model.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * swagger枚举类注解
+ *
+ * @author lixing
+ * @version V1.0 2021/4/12 10:39 上午
+ **/
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SwaggerDisplayEnum {
+    String type() default "type";
+    String desc() default "desc";
+}

+ 228 - 0
apm-common/src/main/java/com/persagy/apm/common/model/data/DataType.java

@@ -0,0 +1,228 @@
+package com.persagy.apm.common.model.data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 数据类型常量
+ *
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+public enum DataType {
+
+    /** 未知数据类型 */
+    UNKNOWN("未知类型"),
+
+    /** 字符串数据类型 */
+    STRING("字符串"),
+
+    /** 字节数据类型 */
+    BYTE("字节"),
+
+    /** 短整数类型 */
+    SHORT("短整数"),
+
+    /** 整数类型 */
+    INT("整数"),
+
+    /** 长整数类型 */
+    LONG("长整数"),
+
+    /** 浮点类型 */
+    FLOAT("浮点"),
+
+    /** 双精度浮点类型 */
+    DOUBLE("双精度浮点"),
+
+    /** 大数字类型 */
+    BIGDECIMAL("大数字"),
+
+    /** 布尔类型 */
+    BOOLEAN("布尔"),
+
+    /** 日期类型 */
+    DATE("日期"),
+
+    /** 时间类型 */
+    TIME("时间"),
+
+    /** 日期时间类型 */
+    DATETIME("日期时间"),
+
+    /** 二进制类型类型 */
+    BINARY("二进制类型");
+
+    /** 类型名称 */
+    private String name;
+
+    /**
+     * 构造方法
+     * @param name 名称
+     */
+    private DataType(String name) {
+        this.name = name;
+    }
+
+    /**
+     * 取得类型名称
+     * @return 类型名称
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * 判断是否为数字类型
+     * @param dataType 数据类型
+     * @return 布尔值
+     */
+    public static boolean isNumberType(DataType dataType) {
+        return dataType == BYTE || dataType == SHORT || dataType == INT || dataType == LONG || dataType == FLOAT || dataType == DOUBLE || dataType == BIGDECIMAL;
+    }
+
+    /**
+     * 判断是否是时间类型
+     * @param dataType 数据类型
+     * @return 布尔值
+     */
+    public static boolean isDateType(DataType dataType) {
+        return dataType == DATE || dataType == DATETIME || dataType == TIME;
+    }
+
+    /**
+     * 判断类是否为值类型
+     * @param clazz 类对象
+     * @return 布尔值
+     */
+    public static boolean isBaseDataType(Class<?> clazz) {
+        return clazz.isPrimitive() || clazz.equals(String.class) || (Number.class).isAssignableFrom(clazz) || (Boolean.class).isAssignableFrom(clazz) || (Date.class).isAssignableFrom(clazz);
+    }
+
+    /**
+     * 根据类对象取得对应的整型数据类型定义
+     * @param clazz 类对象
+     * @return 对应的数据类型
+     */
+    public static DataType parse(Class<?> clazz) {
+        if (clazz == null) {
+            return UNKNOWN;
+        }
+        if (clazz.equals(String.class)) {
+            return STRING;
+        }
+        if (clazz.equals(Integer.TYPE)) {
+            return INT;
+        }
+        if (clazz.equals(Integer.class)) {
+            return INT;
+        }
+        if (clazz.equals(Long.TYPE)) {
+            return LONG;
+        }
+        if (clazz.equals(Long.class)) {
+            return LONG;
+        }
+        if (clazz.equals(BigDecimal.class)) {
+            return BIGDECIMAL;
+        }
+        if (clazz.equals(Float.TYPE)) {
+            return FLOAT;
+        }
+        if (clazz.equals(Float.class)) {
+            return FLOAT;
+        }
+        if (clazz.equals(Double.TYPE)) {
+            return DOUBLE;
+        }
+        if (clazz.equals(Double.class)) {
+            return DOUBLE;
+        }
+        if (clazz.equals(Boolean.TYPE)) {
+            return BOOLEAN;
+        }
+        if (clazz.equals(Boolean.class)) {
+            return BOOLEAN;
+        }
+        if (clazz.equals(Date.class)) {
+            return DATE;
+        }
+        if (clazz.equals(java.sql.Date.class)) {
+            return DATE;
+        }
+        if (clazz.equals(java.sql.Time.class)) {
+            return TIME;
+        }
+        if (clazz.equals(java.sql.Timestamp.class)) {
+            return DATETIME;
+        }
+        if (clazz.equals(Byte.TYPE)) {
+            return BYTE;
+        }
+        if (clazz.equals(Byte.class)) {
+            return BYTE;
+        }
+        if (clazz.equals(Short.TYPE)) {
+            return SHORT;
+        }
+        if (clazz.equals(Short.class)) {
+            return SHORT;
+        }
+        return UNKNOWN;
+    }
+
+    /**
+     * 数据类型名称转换成数据类型整数
+     * @param type 数据类型名称
+     * @return 数据类型整数
+     */
+    public static DataType parse(String type) {
+        if (type == null) {
+            return UNKNOWN;
+        }
+        try {
+            type = type.toUpperCase();
+            if ("INTEGER".equals(type)) {
+                return INT;
+            }
+            return DataType.valueOf(type.toUpperCase());
+        } catch (IllegalArgumentException e) {
+            return UNKNOWN;
+        }
+    }
+
+    /**
+     * 数据类型转换成对应的类对象
+     * @param type 数据类型整数
+     * @return 类对象
+     */
+    public static Class<?> typeToClass(DataType type) {
+        switch (type) {
+            case STRING:
+                return String.class;
+            case INT:
+                return Integer.class;
+            case LONG:
+                return Long.class;
+            case BIGDECIMAL:
+                return BigDecimal.class;
+            case BOOLEAN:
+                return Boolean.class;
+            case FLOAT:
+                return Float.class;
+            case DOUBLE:
+                return Double.class;
+            case DATE:
+            case TIME:
+            case DATETIME:
+                return Date.class;
+            case BYTE:
+                return Byte.class;
+            case SHORT:
+                return Short.class;
+            default:
+                return null;
+        }
+    }
+}

+ 27 - 0
apm-common/src/main/java/com/persagy/apm/common/model/dto/DmpBaseQueryDTO.java

@@ -0,0 +1,27 @@
+package com.persagy.apm.common.model.dto;
+
+import lombok.Data;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @description: 数据中台查询基础类
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/8 11:56 上午
+ * @version: V1.0
+ **/
+@Data
+public class DmpBaseQueryDTO<T> {
+    /** 分页页码 */
+    private Integer page;
+    /** 分页大小 */
+    private Integer size;
+    /** 扩展字段 */
+    private Set<String> withColumns;
+    /** 排序字段 */
+    private List<DmpOrderDTO> orders;
+    /** 查询条件 */
+    private T criteria;
+}

+ 16 - 0
apm-common/src/main/java/com/persagy/apm/common/model/dto/DmpOrderDTO.java

@@ -0,0 +1,16 @@
+package com.persagy.apm.common.model.dto;
+
+import lombok.Data;
+
+/**
+ * @description: 数据中台排序对象
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/8 12:00 下午
+ * @version: V1.0
+ **/
+@Data
+public class DmpOrderDTO {
+    private String column;
+    private Boolean asc;
+}

+ 22 - 0
apm-common/src/main/java/com/persagy/apm/common/model/dto/DmpRequiredParam.java

@@ -0,0 +1,22 @@
+package com.persagy.apm.common.model.dto;
+
+import lombok.Data;
+
+/**
+ * @description: 数据中台基础类
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/4 10:05 上午
+ * @version: V1.0
+ **/
+@Data
+public class DmpRequiredParam {
+
+    private String userId;
+
+    private String appId;
+
+    private String groupCode;
+
+    private String projectId;
+}

+ 23 - 0
apm-common/src/main/java/com/persagy/apm/common/model/dto/EnumQueryDTO.java

@@ -0,0 +1,23 @@
+package com.persagy.apm.common.model.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NonNull;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 枚举类查询DTO
+ *
+ * @author lixing
+ * @version V1.0 2021/3/12 6:26 下午
+ **/
+@Data
+@ApiModel
+public class EnumQueryDTO {
+
+    @NotNull(message = "枚举类型不能为空")
+    @ApiModelProperty("枚举类型")
+    private String type;
+}

+ 50 - 0
apm-common/src/main/java/com/persagy/apm/common/model/dto/PageDTO.java

@@ -0,0 +1,50 @@
+package com.persagy.apm.common.model.dto;
+
+import com.persagy.apm.common.constant.PageQueryConstants;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import net.minidev.json.annotate.JsonIgnore;
+
+import java.util.List;
+
+/**
+ * 分页条件
+ *
+ * @author lixing
+ * @version V1.0 2021/4/29 2:57 下午
+ */
+@Data
+@ApiModel
+public class PageDTO {
+    @ApiModelProperty(value = "当前页数")
+    private Integer page;
+
+    @ApiModelProperty(value = "每页条数")
+    private Integer size;
+    @ApiModelProperty(value = "排序方式")
+    private List<Sort> orders;
+
+    @JsonIgnore
+    @ApiModelProperty(hidden = true)
+    private Integer startIndex;
+
+    public Integer getStartIndex() {
+        return (getPage() - 1) * getSize();
+    }
+
+    public Integer getPage() {
+        if (this.page == null) {
+            return PageQueryConstants.DEFAULT_PAGE;
+        }
+        return this.page;
+    }
+
+    public Integer getSize() {
+        if (this.size == null) {
+            return PageQueryConstants.DEFAULT_SIZE;
+        }
+        return this.size;
+    }
+}
+

+ 26 - 0
apm-common/src/main/java/com/persagy/apm/common/model/dto/Sort.java

@@ -0,0 +1,26 @@
+package com.persagy.apm.common.model.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @description: 列表排序类
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/10/13 3:27 下午
+ * @version: V1.0
+ */
+@NoArgsConstructor
+@Data
+public class Sort {
+    @ApiModelProperty(value = "排序字段名称")
+    private String column;
+    @ApiModelProperty(value = "是否升序排列")
+    private boolean asc;
+
+    public Sort(String column, boolean asc) {
+        this.column = column;
+        this.asc = asc;
+    }
+}

+ 54 - 0
apm-common/src/main/java/com/persagy/apm/common/model/entity/AuditableEntity.java

@@ -0,0 +1,54 @@
+package com.persagy.apm.common.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 审计信息父类
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public abstract class AuditableEntity<T> extends BaseEntity<T> implements IAuditableEntity {
+
+    /** 序列id */
+    private static final long serialVersionUID = -6039076670318997447L;
+
+    /** 创建时间 */
+    public static final String PROP_CREATIONTIME = "creation_time";
+    public static final String DO_PROP_CREATIONTIME = "creationTime";
+
+    /** 创建人 */
+    public static final String PROP_CREATOR = "creator";
+
+    /** 最后一次修改时间 */
+    public static final String PROP_MODIFIEDTIME = "modified_time";
+    public static final String DO_PROP_MODIFIEDTIME = "modifiedTime";
+
+    /** 最后一个修改人 */
+    public static final String PROP_MODIFIER = "modifier";
+
+    @JsonIgnore
+    @TableField(fill = FieldFill.INSERT)
+    @ApiModelProperty(value = "创建时间", hidden = true)
+    protected Date creationTime;
+
+    @ApiModelProperty("创建人")
+    protected String creator;
+
+    @JsonIgnore
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @ApiModelProperty(value = "最后一次修改时间", hidden = true)
+    protected Date modifiedTime;
+
+    @ApiModelProperty("最后一次修改人")
+    protected String modifier;
+
+}

+ 106 - 0
apm-common/src/main/java/com/persagy/apm/common/model/entity/BaseEntity.java

@@ -0,0 +1,106 @@
+package com.persagy.apm.common.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 实体基类RequiredParamsStorage
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@Data
+@ApiModel
+public abstract class BaseEntity<T> extends Model implements IBaseEntity,Serializable,Cloneable{
+
+	/** 序列id */
+	private static final long serialVersionUID = -3231114188611235386L;
+
+	/** 实体属性主键id */
+	public final static String PROP_ID = "id";
+
+	/** 实体属性版本 */
+	public final static String PROP_TS = "ts";
+
+	/** 实体属性实体状态 */
+	public final static String PROP_VALID = "valid";
+
+	/** hash码 */
+	@Getter(AccessLevel.NONE)
+	@Setter(AccessLevel.NONE)
+	@TableField(exist=false)
+	private int hashCode = Integer.MIN_VALUE;
+
+	/** 实体类必须有的属性 */
+
+	@TableId(type = IdType.ASSIGN_ID)
+	@ApiModelProperty("主键")
+	protected String id;
+
+	/** 时间戳 */
+	@ApiModelProperty("乐观锁")
+	protected Date ts;
+
+	/** 实体状态 - 有效标识 */
+	@TableField(fill = FieldFill.INSERT)
+	@ApiModelProperty(value = "删除标记", notes = "com.persagy.apm.common.constant.enums.ValidEnum")
+	protected Integer valid;
+
+	@Override
+	public boolean equals (Object obj) {
+		if (null == obj) {
+            return false;
+        }
+		if (!(obj instanceof BaseEntity)) {
+			return false;
+		} else {
+			BaseEntity entity = (BaseEntity) obj;
+			if (null == this.getId() || null == entity.getId()) {
+				return false;
+			} else {
+				return (this.getId().equals(entity.getId()));
+			}
+		}
+	}
+
+	@Override
+	public int hashCode () {
+		if (Integer.MIN_VALUE != this.hashCode) {
+			return this.hashCode;
+		}
+		if (null == this.getId()) {
+			return super.hashCode();
+		}
+		String hashStr = this.getClass().getName() + ":" + this.getId().hashCode();
+		this.hashCode = hashStr.hashCode();
+		return this.hashCode;
+	}
+
+	@Override
+	public int compareTo (IBaseEntity obj) {
+		if (obj.hashCode() > hashCode()) {
+			return 1;
+		} else if (obj.hashCode() < hashCode()) {
+			return -1;
+		} else {
+			return 0;
+		}
+	}
+
+	@Override
+	public Object clone() throws CloneNotSupportedException {
+		return super.clone();
+	}
+
+}

+ 59 - 0
apm-common/src/main/java/com/persagy/apm/common/model/entity/IAuditableEntity.java

@@ -0,0 +1,59 @@
+package com.persagy.apm.common.model.entity;
+
+import java.util.Date;
+
+/**
+ * 审计信息类接口
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public interface IAuditableEntity extends IBaseEntity{
+
+	/**
+	 * 取得创建时间
+	 * @return 创建时间
+	 */
+	Date getCreationTime();
+
+	/**
+	 * 设置创建时间,记录插入时有效
+	 * @param creationtime 创建时间
+	 */
+	void setCreationTime(Date creationtime);
+
+	/**
+	 * 取得创建实体的操作员的登录名
+	 * @return 操作员标识
+	 */
+	String getCreator() ;
+
+	/**
+	 * 设置创建实体的操作员标识,记录插入时有效
+	 * @param creator 操作员标识
+	 */
+	void setCreator(String creator);
+
+	/**
+	 * 取得最后修改时间.
+	 * @return 最后修改时间
+	 */
+	Date getModifiedTime();
+
+	/**
+	 * 设置最后修改时间.记录更新时有效
+	 * @param modifiedtime 最后修改时间
+	 */
+	void setModifiedTime(Date modifiedtime);
+
+	/**
+	 * 取得最后修改的操作员的登录名.
+	 * @return 操作员的登录名.
+	 */
+	String getModifier();
+
+	/**
+	 * 设置最后修改的操作员的登录名,记录更新时有效
+	 * @param modifier 操作员的登录名.
+	 */
+	void setModifier(String modifier);
+}

+ 49 - 0
apm-common/src/main/java/com/persagy/apm/common/model/entity/IBaseEntity.java

@@ -0,0 +1,49 @@
+package com.persagy.apm.common.model.entity;
+
+
+import java.util.Date;
+
+/**
+ * 基础实体接口
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public interface IBaseEntity extends Comparable<IBaseEntity>{
+
+	/**
+	 * 取得实体主键
+	 * @return 实体主键
+	 */
+	String getId();
+
+	/**
+	 * 设置实体主键
+	 * @param id 实体主键
+	 */
+	void setId(String id);
+
+	/**
+	 * 取得时间戳
+	 * @return 时间戳
+	 */
+	Date getTs();
+
+	/**
+	 * 设置时间戳
+	 * @param ts 时间戳
+	 */
+	void setTs(Date ts);
+
+	/**
+	 * 取得实体状态
+	 * @return 实体状态
+	 */
+	Integer getValid();
+
+	/**
+	 * 设置实体状态
+	 * @param status 实体状态
+	 */
+	void setValid(Integer status);
+	
+}

+ 36 - 0
apm-common/src/main/java/com/persagy/apm/common/model/entity/ITreeEntity.java

@@ -0,0 +1,36 @@
+package com.persagy.apm.common.model.entity;
+
+/**
+ * 树形接口
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public interface ITreeEntity extends IBaseEntity {
+
+    /** 实体属性树形编码 */
+    String PROP_INNER_CODE = "innerCode";
+
+    /**
+     * 取得树形编码
+     * @return 树形编码
+     */
+    String getInnerCode();
+
+    /**
+     * 设置树形编码
+     * @param innerCode 设置树形编码
+     */
+    void setInnerCode(String innerCode);
+
+    /**
+     * 取得innercode属性名
+     * @return
+     */
+    String getInnerCodeField();
+
+    /**
+     * 取得当前实体表名
+     * @return 表名
+     */
+    String getTreeTableName();
+}

+ 106 - 0
apm-common/src/main/java/com/persagy/apm/common/model/query/ChainLogicExpression.java

@@ -0,0 +1,106 @@
+package com.persagy.apm.common.model.query;
+
+import org.apache.commons.collections4.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 查询条件逻辑表达式表达式
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+public class ChainLogicExpression implements IExpression {
+
+	/**
+	 * 序列id
+	 */
+	private static final long serialVersionUID = 5682864471973760964L;
+
+	/**
+	 * 表达式列表
+	 */
+	private List<IExpression> expressions = null;
+	
+	/**
+	 * 操作符
+	 */
+	private boolean isOr = false;
+	
+	/**
+	 * 构造方法
+	 */
+	public ChainLogicExpression(){
+		
+	}
+	
+	/**
+	 * 表达式构造方法
+	 * @param isOr       是否为逻辑或相连
+	 * @param expressions 表达式数组
+	 */
+	public ChainLogicExpression(boolean isOr, IExpression...expressions){
+		addExpression(expressions);
+		setOr(isOr);
+	}
+
+	/**
+	 * 取得链式逻辑表达式中包含的表达式列表
+	 * @return 表达式列表
+	 */
+	public List<IExpression> getExpressions() {
+		return expressions;
+	}
+
+	/**
+	 * 设置链式逻辑表达式中包含的表达式列表
+	 * @param expressions 表达式列表
+	 */
+	public void setExpressions(List<IExpression> expressions) {
+		this.expressions = expressions;
+	}
+	
+	/**
+	 * 添加表达式到链式表达式中
+	 * @param expressions 表达式数组
+	 */
+	public void addExpression(IExpression... expressions){
+		if(this.expressions == null) {
+            this.expressions = new ArrayList<>();
+        }
+		CollectionUtils.addAll(this.expressions, expressions);
+	}
+	
+	/**
+	 * 判断是否为‘或’逻辑操作
+	 * @return 布尔值
+	 */
+	public boolean isOr() {
+		return isOr;
+	}
+
+	/**
+	 * 设置是否为‘或’逻辑操作
+	 * @param isOr 布尔值
+	 */
+	public void setOr(boolean isOr) {
+		this.isOr = isOr;
+	}
+
+	@Override
+	public ExpressionType getType() {
+		return ExpressionType.CHAIN;
+	}
+
+	/**
+	 * 取得表达式大小
+	 * @return 表达式大小
+	 */
+	public int size(){
+		if(expressions == null) {
+			return 0;
+		}
+		return expressions.size();
+	}
+}

+ 238 - 0
apm-common/src/main/java/com/persagy/apm/common/model/query/Condition.java

@@ -0,0 +1,238 @@
+package com.persagy.apm.common.model.query;
+
+import com.persagy.apm.common.model.data.DataType;
+import lombok.Data;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 查询类,封装查询条件
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+@Data
+public class Condition implements Serializable {
+    /**
+     * 序列id
+     */
+    private static final long serialVersionUID = 6256786995910707674L;
+    /**
+     * 条件列表
+     */
+    private List<IExpression> expressions = null;
+    /**
+     * 自由条件列表
+     */
+    private List<String> freeConditions = null;
+    /**
+     * 自由搜索值
+     */
+    private String freeValue = null;
+
+    /**
+     * 前置逻辑操作符,为空时不增加逻辑操作符
+     */
+    private String preLogic = null;
+
+    /**
+     * 用于属性别名转换的别名定义,字符串形式如:User as u,
+     * 该别名应用场景,如果定义的条件名为User.name 实际使用的表别名为u,需要在查询执行时进行替换
+     * User.name将被替换成u.name
+     */
+    private String[] fromAlias = null;
+
+    /**
+     * 无条件时是否忽略条件,还是变成不成立条件
+     */
+    private boolean ignore = true;
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     */
+    public void addExpressions(IExpression... condition) {
+        if (this.expressions == null) {
+            this.expressions = new ArrayList<IExpression>();
+        }
+        if (this.expressions.size() == 0) {
+            CollectionUtils.addAll(this.expressions, condition);
+        } else {
+            for (IExpression con : condition) {
+                if (this.expressions.indexOf(con) < 0) {
+                    this.expressions.add(con);
+                }
+            }
+        }
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     * @param dataType  数据类型
+     * @param operator  操作符
+     */
+    public void addExpression(String condition, Object value, String dataType, String operator) {
+        ValueExpression exp = new ValueExpression(condition, value, dataType, operator);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     * @param operator  操作符
+     */
+    public void addExpression(String condition, Object value, String operator) {
+        ValueExpression exp = new ValueExpression(condition, value, operator);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     * @param dataType  数据类型
+     * @param operator  操作符
+     */
+    public void addExpression(String condition, Object value, DataType dataType, Operator operator) {
+        ValueExpression exp = new ValueExpression(condition, value, dataType, operator);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     * @param operator  操作符
+     */
+    public void addExpression(String condition, Object value, Operator operator) {
+        ValueExpression exp = new ValueExpression(condition, value, operator);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     */
+    public void addExpression(String condition, Object value) {
+        ValueExpression exp = new ValueExpression(condition, value);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 设置自由条件属性列表
+     *
+     * @param freeConditions 自由条件属性列表
+     */
+    public void setFreeConditions(String... freeConditions) {
+        List<String> freeCons = new ArrayList<String>();
+        CollectionUtils.addAll(freeCons, freeConditions);
+        this.freeConditions = freeCons;
+    }
+
+    /**
+     * 添加自由条件属性
+     *
+     * @param freeCondition 自由条件属性
+     */
+    public void setFreeExpressions(String freeValue, String... freeCondition) {
+        if (freeCondition == null || freeCondition.length == 0 || StringUtils.isEmpty(freeValue)) {
+            return;
+        }
+        setFreeConditions(freeCondition);
+        setFreeValue(freeValue);
+    }
+
+    /**
+     * 添加自由条件属性
+     *
+     * @param freeCondition 自由条件属性
+     */
+    public void addFreeCondition(String... freeCondition) {
+        if (this.freeConditions == null) {
+            this.freeConditions = new ArrayList<String>();
+        }
+        if (this.freeConditions.size() == 0) {
+            CollectionUtils.addAll(this.freeConditions, freeCondition);
+        } else {
+            for (String con : freeCondition) {
+                if (this.freeConditions.indexOf(con) < 0) {
+                    this.freeConditions.add(con);
+                }
+            }
+        }
+    }
+
+    /**
+     * 转换成表达式列表
+     *
+     * @return 表达式列表
+     */
+    public List<IExpression> toExpressions() {
+        List<IExpression> cons = getExpressions();
+        if (StringUtils.isEmpty(getFreeValue())) {
+            return cons;
+        }
+        ChainLogicExpression expression = new ChainLogicExpression();
+        if (cons != null && cons.size() > 0) {
+            for (IExpression con : cons) {
+                expression.addExpression(con);
+            }
+        }
+        List<String> freeCons = getFreeConditions();
+        String freeValue = getFreeValue();
+        if (freeCons != null && freeCons.size() > 0 && StringUtils.isNotEmpty(freeValue)) {
+            ChainLogicExpression freeExp = new ChainLogicExpression(true);
+            for (String freeCon : freeCons) {
+                IExpression exp = new ValueExpression(freeCon, freeValue, Operator.LIKEANY);
+                freeExp.addExpression(exp);
+            }
+            expression.addExpression(freeExp);
+        }
+        List<IExpression> expressions = new ArrayList<>();
+        expressions.add(expression);
+        return expressions;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+
+        List<IExpression> es = toExpressions();
+        if (es != null && es.size() > 0) {
+            if (preLogic != null) {
+                builder.append(preLogic).append(StringUtils.SPACE);
+            }
+            builder.append(StringUtils.join(es, " and "));
+            return builder.toString();
+        } else {
+            return StringUtils.EMPTY;
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj instanceof Condition)) {
+            return false;
+        }
+        return obj.toString().equals(toString());
+    }
+
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+}

+ 29 - 0
apm-common/src/main/java/com/persagy/apm/common/model/query/ExpressionType.java

@@ -0,0 +1,29 @@
+package com.persagy.apm.common.model.query;
+
+/**
+ * 表达式类型
+ * @author Sun
+ * @version 1.0 2017-9-2
+ * @since 1.7
+ */
+public enum ExpressionType {
+
+    /**
+     * 简单值表达式
+     */
+    VALUE,
+    /**
+     * 逻辑表达式“与或”
+     */
+    LOGIC,
+
+    /**
+     * 链式逻辑表达式“与或”,表达式列表中的表达式并列与或者或
+     */
+    CHAIN,
+
+    /**
+     * sql表达式,只能在后端使用
+     */
+    SQL
+}

+ 19 - 0
apm-common/src/main/java/com/persagy/apm/common/model/query/IExpression.java

@@ -0,0 +1,19 @@
+package com.persagy.apm.common.model.query;
+
+import java.io.Serializable;
+
+
+/**
+ * 表达式接口
+ * @author Sun
+ * @version 1.0 2014年5月9日
+ * @since 1.7
+ */
+public interface IExpression extends Serializable {
+
+    /**
+     * 取得表达式类型
+     * @return 表达式类型
+     */
+    ExpressionType getType();
+}

+ 109 - 0
apm-common/src/main/java/com/persagy/apm/common/model/query/LogicExpression.java

@@ -0,0 +1,109 @@
+package com.persagy.apm.common.model.query;
+
+/**
+ * 查询条件逻辑表达式表达式
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+public class LogicExpression implements IExpression {
+
+    /**
+     * 序列id
+     */
+    private static final long serialVersionUID = -1756448873214671761L;
+
+    /**
+     * 左侧表达式
+     */
+    private IExpression left = null;
+
+    /**
+     * 右侧表达式
+     */
+    private IExpression right = null;
+
+    /**
+     * 操作符
+     */
+    private boolean isOr = false;
+
+    /**
+     * 构造方法
+     */
+    public LogicExpression() {
+
+    }
+
+    /**
+     * 表达式构造方法
+     *
+     * @param left 左侧表达式
+     * @param right        右侧表达式
+     * @param isOr     是否为或逻辑
+     */
+    public LogicExpression(IExpression left, IExpression right, boolean isOr) {
+        setLeft(left);
+        setRight(right);
+        setOr(isOr);
+    }
+
+    /**
+     * 取得左侧表达式
+     *
+     * @return 左侧表达式
+     */
+    public IExpression getLeft() {
+        return left;
+    }
+
+    /**
+     * 设置左侧表达式
+     *
+     * @param left 左侧表达式
+     */
+    public void setLeft(IExpression left) {
+        this.left = left;
+    }
+
+    /**
+     * 取得右侧表达式
+     *
+     * @return 右侧表达式
+     */
+    public IExpression getRight() {
+        return right;
+    }
+
+    /**
+     * 设置右侧表达式
+     *
+     * @param right 右侧表达式
+     */
+    public void setRight(IExpression right) {
+        this.right = right;
+    }
+
+    /**
+     * 判断是否为‘或’逻辑操作
+     *
+     * @return 布尔值
+     */
+    public boolean isOr() {
+        return isOr;
+    }
+
+    /**
+     * 设置是否为‘或’逻辑操作
+     *
+     * @param isOr 布尔值
+     */
+    public void setOr(boolean isOr) {
+        this.isOr = isOr;
+    }
+
+    @Override
+    public ExpressionType getType() {
+        return ExpressionType.LOGIC;
+    }
+}

+ 161 - 0
apm-common/src/main/java/com/persagy/apm/common/model/query/Operator.java

@@ -0,0 +1,161 @@
+/*
+ * Copyright(c) FastSpace Software 2014. All Rights Reserved.
+ */
+package com.persagy.apm.common.model.query;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * 操作符枚举
+ *
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+public enum Operator {
+    /*操作符常量*/
+    /**
+     * =操作符
+     */
+    EQUAL("=", "等于"),
+
+    /**
+     * <>操作符
+     */
+    NOTEQUAL("<>", "不等于"),
+
+    /**
+     * >操作符
+     */
+    GREATER(">", "大于"),
+
+    /**
+     * <操作符
+     */
+    LESS("<", "小于"),
+
+    /**
+     * >=操作符
+     */
+    GREATEREQUAL(">=", "大于等于"),
+
+    /**
+     * <=操作符
+     */
+    LESSEQAUL("<=", "小于等于"),
+
+
+    /**
+     * like操作符,全模糊匹配
+     */
+    LIKEACC("likea", "精确包含"),//前后没有%
+    NOTLIKEACC("not likea", "不精确包含"),//前后没有%
+    LIKEANY("like", "包含"),
+    NOTLIKEANY("not like", "不包含"),
+
+    /**
+     * like操作符,开始匹配,结尾模糊
+     */
+    LIKESTART("likes", "左包含"),
+    NOTLIKESTART("not likes", "不左包含"),
+
+    /**
+     * like操作符,结尾匹配,开始模糊
+     */
+    LIKEEND("likee", "右包含"),
+    NOTLIKEEND("not likee", "不右包含"),
+
+    /**
+     * is null操作符
+     */
+    ISNULL("is null", "为空值"),
+
+    /**
+     * is not null操作符
+     */
+    ISNOTNULL("is not null", "不为空值"),
+
+    /**
+     * in操作符号
+     */
+    IN("in", "列举"),
+
+    /**
+     * not in操作符号
+     */
+    NOTIN("not in", "反列举");
+
+    /**
+     * 操作符编码
+     */
+    private String code;
+
+    /**
+     * 操作符名称
+     */
+    private String name;
+
+    /**
+     * 构造方法
+     *
+     * @param code 编码
+     * @param name 名称
+     */
+    private Operator(String code, String name) {
+        this.code = code;
+        this.name = name;
+    }
+
+    /**
+     * 取得类型编码
+     *
+     * @return 类型编码
+     */
+    public String getCode() {
+        return code;
+    }
+
+    /**
+     * 取得类型名称
+     *
+     * @return 类型名称
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * (non-Javadoc)
+     *
+     * @see Enum#toString()
+     */
+    @Override
+    public String toString() {
+        return getCode();
+    }
+
+    /**
+     * 根据编码获取操作符
+     *
+     * @param code 操作符编码
+     * @return 操作符
+     */
+    public static Operator parse(String code) {
+        code = code.toLowerCase();
+        if (!STR_VAL_MAP.containsKey(code)) {
+            throw new IllegalArgumentException("Unknown String Value: " + code);
+        }
+        return STR_VAL_MAP.get(code);
+    }
+
+    private static final Map<String, Operator> STR_VAL_MAP;
+
+    static {
+        STR_VAL_MAP = new HashMap<String, Operator>();
+        for (final Operator en : Operator.values()) {
+            STR_VAL_MAP.put(en.getCode(), en);
+        }
+    }
+}

+ 64 - 0
apm-common/src/main/java/com/persagy/apm/common/model/query/SqlExpression.java

@@ -0,0 +1,64 @@
+package com.persagy.apm.common.model.query;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+
+/**
+ * 基于sql表达式,只能后台添加
+ * @author Sun
+ * @version 1.0 2014年5月9日
+ * @since 1.7
+ */
+@Data
+public class SqlExpression implements IExpression,Serializable{
+
+
+	/**
+	 * 序列id
+	 */
+	private static final long serialVersionUID = 7826974743759458453L;
+
+	/**
+	 * sql语句
+	 */
+	private String sql = null;
+
+
+	/**
+	 * 构造方法
+	 */
+	public SqlExpression(){
+	}
+
+	/**
+	 * 表达式构造方法,操作符为等于
+	 * @param sql sql语句
+	 */
+	public SqlExpression(String sql){
+		this.sql = sql;
+	}
+
+	@Override
+	public int hashCode() {
+		return toString().hashCode();
+	}
+
+
+	@Override
+	public boolean equals(Object obj) {
+		return obj instanceof SqlExpression && this.toString().equals(obj.toString());
+	}
+
+	@Override
+	public String toString() {
+		return getSql();
+	}
+
+
+	@Override
+	public ExpressionType getType() {
+		return ExpressionType.SQL;
+	}
+}

+ 255 - 0
apm-common/src/main/java/com/persagy/apm/common/model/query/ValueExpression.java

@@ -0,0 +1,255 @@
+package com.persagy.apm.common.model.query;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.persagy.apm.common.model.data.DataType;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.regex.Pattern;
+
+
+/**
+ * 简单值查询表达式
+ * @author Sun
+ * @version 1.0 2014年5月9日
+ * @since 1.7
+ */
+public class ValueExpression implements IExpression{
+
+	/**
+	 * 序列id
+	 */
+	private static final long serialVersionUID = -1127113431925423258L;
+
+	/**
+	 * 查询的实体属性名
+	 */
+	private String propertyName = null;
+	
+	/**
+	 * 属性名测试正则表达式
+	 */
+	private static Pattern propertyParttern = Pattern.compile("^\\w+[\\w+\\.]*\\w*$");
+	
+	/**
+	 * 数据类型
+	 */
+	private DataType dataType = DataType.STRING;
+	
+	/**
+	 * 表达式值
+	 */
+	private Object value = null;
+	
+	/**
+	 * 操作符
+	 */
+	private Operator operator = Operator.EQUAL;
+	
+	/**
+	 * 构造方法
+	 */
+	public ValueExpression(){
+	}
+	
+	/**
+	 * 表达式构造方法,操作符为等于
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 */
+	public ValueExpression(String propertyName, Object value){
+		this(propertyName,value,Operator.EQUAL);
+	}
+	/**
+	 * 表达式构造方法
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 * @param operator 操作符
+	 */
+	public ValueExpression(String propertyName, Object value, Operator operator){
+		setPropertyName(propertyName);
+		setOperator(operator);
+		setValue(value);
+	}
+
+	/**
+	 * 表达式构造方法
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 * @param operator 操作符
+	 */
+	public ValueExpression(String propertyName, Object value, String operator){
+		setPropertyName(propertyName);
+		setOperator(Operator.parse(operator));
+		setValue(value);
+	}
+	/**
+	 * 表达式构造方法
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 * @param operator 操作符
+	 */
+	public ValueExpression(String propertyName, Object value, DataType dataType,Operator operator){
+		setPropertyName(propertyName);
+		if(dataType != DataType.UNKNOWN) {
+            setDataType(dataType);
+        }
+		setOperator(operator);
+		setValue(value);
+	}
+
+	/**
+	 * 表达式构造方法
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 * @param operator 操作符
+	 */
+	public ValueExpression(String propertyName, Object value, String dataType, String operator){
+		setPropertyName(propertyName);
+		if(dataType != null){
+			 DataType dt =  DataType.parse(dataType);
+			 if(dt != DataType.UNKNOWN) {
+                 setDataType(dt);
+             }
+		}
+		setOperator(Operator.parse(operator));
+		setValue(value);
+	}
+	/**
+	 * 取得表达式属性名
+	 * @return 属性名
+	 */
+	public String getPropertyName() {
+		return propertyName;
+	}
+
+	/**
+	 * 设置表达式属性名
+	 * @param propertyName 属性名
+	 */
+	public void setPropertyName(String propertyName) {
+		if(propertyName.contains(" ")){
+			String[] temps = propertyName.split(" ");
+			propertyName = temps[0];
+			setDataType(DataType.parse(temps[1]));
+			if(temps.length > 2){
+				setOperator(Operator.parse(temps[2]));
+			}
+		}
+		if(!propertyParttern.matcher(propertyName).matches()) {
+            throw new RuntimeException("非法查询条件");
+        }
+		this.propertyName = propertyName;
+	}
+
+	/**
+	 * 取得表达式条件值
+	 * @return 条件值
+	 */
+	public Object getValue() {
+		return value;
+	}
+
+	/**
+	 * 设置表达式条件值
+	 * @param value 条件值
+	 */
+	public void setValue(Object value) {
+		if(this.operator != Operator.ISNULL || this.operator == Operator.ISNOTNULL) {
+            this.value = value;
+        }
+	}
+
+	/**
+	 * 取得表达式操作符
+	 * @return 表达式操作符
+	 */
+	public Operator getOperator() {
+		return operator;
+	}
+
+	/**
+	 * 设置表达式操作符
+	 * @param operator 表达式操作符
+	 */
+	public void setOperator(Operator operator) {
+		if(this.operator == Operator.ISNULL || this.operator == Operator.ISNOTNULL) {
+            this.value = null;
+        }
+		this.operator = operator;
+	}
+
+	
+	/**
+	 * 取得字段数据类型
+	 * @return 字段数据类型
+	 */
+	public DataType getDataType() {
+		return dataType;
+	}
+
+	/**
+	 * 设置字段数据类型
+	 * @param dataType 字段数据类型
+	 */
+	public void setDataType(DataType dataType) {
+		if(dataType==DataType.UNKNOWN) {
+            return;
+        }
+		this.dataType = dataType;
+	}
+
+	@Override
+	public ExpressionType getType() {
+		return ExpressionType.VALUE;
+	}
+
+	/**
+	 * (non-Javadoc)
+	 * @see Object#hashCode()
+	 */
+	@Override
+	public int hashCode() {
+		return toString().hashCode();
+	}
+
+	/**
+	 * (non-Javadoc)
+	 * @see Object#equals(Object)
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if(!(obj instanceof ValueExpression)) {
+            return false;
+        }
+		return this.toString().equals(obj.toString());
+	}
+
+	/**
+	 * (non-Javadoc)
+	 * @see Object#toString()
+	 */
+	@Override
+	public String toString() {
+		if(getValue() == null) {
+            return getPropertyName()+" "+getDataType()+ " " + getOperator();
+        } else {
+            return getPropertyName()+" "+getDataType()+ " " + getOperator()+getValue();
+        }
+	}
+	
+	/**
+	 * 判断当前表达式是否合法
+	 * @return 布尔值
+	 */
+	@JsonIgnore
+	public boolean isValidate(){
+		if(StringUtils.isBlank(propertyName)) {
+            return false;
+        }
+		if(getOperator() == Operator.ISNULL || getOperator() == Operator.ISNOTNULL) {
+            return true;
+        }
+		return value != null;
+	}
+	
+}

+ 30 - 0
apm-common/src/main/java/com/persagy/apm/common/model/vo/BaseVo.java

@@ -0,0 +1,30 @@
+package com.persagy.apm.common.model.vo;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotBlank;
+/***
+ * Description: VO基类
+ * @author : lijie
+ * @date :2021/4/10 14:02
+ * Update By lijie 2021/4/10 14:02
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class BaseVo {
+    /**用户id*/
+    @ApiModelProperty(value = "账号id",required = true)
+    @NotBlank(message = "缺少非空项:user_id")
+    @JsonProperty("user_id")
+    @JsonAlias({"user_id","userId","accountId","account_id"})
+    private String userId;
+
+}

+ 18 - 0
apm-common/src/main/java/com/persagy/apm/common/model/vo/DuplicateVO.java

@@ -0,0 +1,18 @@
+package com.persagy.apm.common.model.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 校验是否重复vo
+ *
+ * @author lixing
+ * @version V1.0 2021/4/7 7:36 下午
+ **/
+@Data
+@ApiModel
+public class DuplicateVO {
+    @ApiModelProperty("是否重复")
+    private Boolean duplicate;
+}

+ 23 - 0
apm-common/src/main/java/com/persagy/apm/common/model/vo/EnumVO.java

@@ -0,0 +1,23 @@
+package com.persagy.apm.common.model.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.checkerframework.checker.units.qual.K;
+
+/**
+ * 枚举vo类
+ *
+ * @author lixing
+ * @version V1.0 2021/3/12 6:24 下午
+ **/
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel
+public class EnumVO {
+    @ApiModelProperty(value = "键")
+    private String key;
+    @ApiModelProperty(value = "值")
+    private String value;
+}

+ 73 - 0
apm-common/src/main/java/com/persagy/apm/common/model/vo/GeneralDictVo.java

@@ -0,0 +1,73 @@
+package com.persagy.apm.common.model.vo;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+
+@EqualsAndHashCode
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class GeneralDictVo {
+    @ApiModelProperty(value = "字典code")
+    private String code;
+
+    @ApiModelProperty(value = "字典名称")
+    private String name;
+
+    @ApiModelProperty(value = "字典描述")
+    private String description;
+
+    @ApiModelProperty(value = "有效值",hidden = true)
+    @JsonIgnore
+    private Boolean valid;
+
+    @ApiModelProperty(value = "字典类型",hidden = true)
+    @JSONField(alternateNames = {"dict_type","dictType"})
+    @JsonProperty("dict_type")
+    private String dictType;
+
+    @ApiModelProperty(value = "默认使用与否",hidden = true)
+    @JSONField(alternateNames = {"default_use","defaultUse"})
+    @JsonIgnore
+    private Boolean defaultUse;
+
+    @ApiModelProperty(value = "字典唯一标识",hidden = true)
+    @JSONField(alternateNames = {"dict_id","dictId"})
+    @JsonIgnore
+    private String dictId;
+
+    @ApiModelProperty(value = "更新时间",hidden = true)
+    @JSONField(alternateNames = {"update_time","updateTime"})
+    @JsonIgnore
+    private String updateTime;
+
+    @ApiModelProperty(value = "创建时间",hidden = true)
+    @JSONField(alternateNames = {"create_time","createTime"})
+    @JsonIgnore
+    private String createTime;
+
+    @ApiModelProperty(value = "扩展字段",hidden = true)
+    @JSONField(alternateNames = {"extend_field","extendField"})
+    @JsonIgnore
+    private String extendField;
+
+    @ApiModelProperty(value = "自定义名称",hidden = true)
+    @JSONField(alternateNames = {"customer_name","customerName"})
+    @JsonIgnore
+    private String customerName;
+
+    @ApiModelProperty(value = "是否使用自定义名称",hidden = true)
+    @JSONField(alternateNames = {"customer_use","customerUse"})
+    @JsonIgnore
+    private String customerUse;
+
+    @ApiModelProperty(value = "排序",hidden = true)
+    @JsonIgnore
+    private Integer order;
+
+
+}

+ 80 - 0
apm-common/src/main/java/com/persagy/apm/common/model/vo/OldResponseResult.java

@@ -0,0 +1,80 @@
+package com.persagy.apm.common.model.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.persagy.common.utils.ResponseResult;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @description 
+ * @author zhangqiankun
+ * @since 2020年8月26日:	下午5:17:08
+ */
+@Data
+@ToString
+public class OldResponseResult<T> extends ResponseResult implements Serializable {
+	private static final long serialVersionUID = -5424463986046237843L;
+	/**结果code*/
+	@ApiModelProperty("结果code")
+	@JsonProperty("ResultCode")
+	private String respCode;
+	/**结果标志:success-成功,failure-失败*/
+	@ApiModelProperty("结果标志:success-成功,failure-失败")
+	@JsonProperty("Result")
+	private String result;
+	/**响应描述*/
+	@ApiModelProperty("响应描述")
+	@JsonProperty("ResultMsg")
+	private String respMsg;
+	/**响应数据数组*/
+	@ApiModelProperty("响应数据数组")
+	@JsonProperty("Content")
+	@JsonInclude(JsonInclude.Include.NON_NULL)
+	private List<T> content;
+	/**响应数据,对象类型*/
+	@ApiModelProperty("响应数据,对象类型")
+	@JsonProperty("Item")
+	@JsonInclude(JsonInclude.Include.NON_NULL)
+	private T item;
+	/**记录总数*/
+	@ApiModelProperty("响应数据条数")
+	@JsonProperty("Count")
+	private Long count;
+	
+	public OldResponseResult() {
+		
+	}
+
+	public OldResponseResult(String respCode, String result, String respMsg) {
+		this.respCode = respCode;
+		this.result=result;
+		this.respMsg = respMsg;
+	}
+	
+	public OldResponseResult(String respCode, String result, String respMsg, List<T> content) {
+		this.respCode = respCode;
+		this.result=result;
+		this.respMsg = respMsg;
+		this.content = content;
+	}
+
+	public OldResponseResult(String respCode, String result, String respMsg, List<T> content, Long count) {
+		this.respCode = respCode;
+		this.result=result;
+		this.respMsg = respMsg;
+		this.content = content;
+		this.count = count;
+	}
+	public OldResponseResult(String respCode, String result, String respMsg, T item) {
+		this.respCode = respCode;
+		this.result=result;
+		this.respMsg = respMsg;
+		this.item = item;
+	}
+
+}

+ 26 - 0
apm-common/src/main/java/com/persagy/apm/common/model/vo/SimpleObjVO.java

@@ -0,0 +1,26 @@
+package com.persagy.apm.common.model.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 简单对象vo类
+ *
+ * @author lixing
+ * @version V1.0 2021/3/27 5:18 下午
+ **/
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel
+public class SimpleObjVO {
+    @ApiModelProperty(value = "主键")
+    private String id;
+    @ApiModelProperty(value = "名称")
+    private String name;
+}

+ 26 - 0
apm-common/src/main/java/com/persagy/apm/common/response/ApmDmpResponseUtil.java

@@ -0,0 +1,26 @@
+package com.persagy.apm.common.response;
+
+import com.persagy.common.constant.DMPConstant;
+import com.persagy.common.utils.DMPResponseUtil;
+import org.apache.poi.ss.formula.functions.T;
+
+/**
+ * apm数据中台结果处理通用类
+ *
+ * @author lixing
+ * @version V1.0 2021/3/16 5:05 下午
+ */
+public class ApmDmpResponseUtil extends DMPResponseUtil {
+
+    /**
+     * 判断数据中台的分页查询是否成功
+     *
+     * @param pageResponse 数据中台分页查询结果
+     * @return true/false
+     * @author lixing
+     * @version V1.0 2021/3/16 5:05 下午
+     */
+    public static boolean isSuccess(DmpPageResponse<T> pageResponse) {
+        return DMPConstant.DMP_RESULT_VAL.equals(pageResponse.getResult());
+    }
+}

+ 23 - 0
apm-common/src/main/java/com/persagy/apm/common/response/ApmResponseUpsertVO.java

@@ -0,0 +1,23 @@
+package com.persagy.apm.common.response;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @description: apm的创建和更新结果VO类
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/10 11:33 上午
+ * @version: V1.0
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ApmResponseUpsertVO {
+    @ApiModelProperty("主键")
+    private String id;
+}

+ 61 - 0
apm-common/src/main/java/com/persagy/apm/common/response/CommonResult.java

@@ -0,0 +1,61 @@
+package com.persagy.apm.common.response;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import static com.persagy.common.enums.ResponseCode.A00000;
+
+/**
+ * 服务调用消息结果
+ * @author Charlie Yu
+ * @date 2021-03-25
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel("响应结果")
+public class CommonResult<T> {
+
+    /** 不需要提示 */
+    public static final CommonResult SUCCESS = new CommonResult();
+
+    @ApiModelProperty(value = "响应码")
+    private String respCode;
+    @ApiModelProperty(value = "响应信息")
+    private String respMsg;
+    @ApiModelProperty(value = "响应数据")
+    private T content;
+
+    /**
+     * 构造方法
+     */
+    public CommonResult(){
+        this(A00000.getCode(), A00000.getDesc());
+    }
+
+    /**
+     * 构造方法
+     * @param respCode 响应码
+     * @param respMsg 提示信息
+     */
+    public CommonResult(String respCode, String respMsg) {
+        this(respCode, respMsg,null);
+    }
+
+    /**
+     * 构造方法
+     * @param respCode 响应码
+     * @param respMsg 提示信息
+     * @param content 数据
+     */
+    public CommonResult(String respCode, String respMsg, T content){
+        this.respCode = respCode;
+        this.respMsg = respMsg;
+        this.content = content;
+    }
+
+    public static <T> CommonResult<T> success(T t) {
+        return  new CommonResult<>(A00000.getCode(), A00000.getDesc(),t);
+    }
+}

+ 31 - 0
apm-common/src/main/java/com/persagy/apm/common/response/DmpPageResponse.java

@@ -0,0 +1,31 @@
+package com.persagy.apm.common.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * @description:
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/8 2:06 下午
+ * @version: V1.0
+ **/
+public class DmpPageResponse<T> {
+    @Getter
+    @Setter
+    private String result;
+
+    @Getter
+    @Setter
+    private String message;
+
+    @Getter
+    @Setter
+    private Long count;
+
+    @Getter
+    @Setter
+    private List<T> data;
+}

+ 48 - 0
apm-common/src/main/java/com/persagy/apm/common/response/FeignResponseContent.java

@@ -0,0 +1,48 @@
+package com.persagy.apm.common.response;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+
+/**
+ * @description: 服务接口调用返回值
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/9/21 11:01
+ * @version: V1.0
+ */
+@Data
+@NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class FeignResponseContent<T> {
+
+    /**
+     * 响应码
+     */
+    private String respCode;
+
+    /**
+     * 成功时返回的列表数据
+     */
+    @JSONField(name = "Content")
+    private T content;
+
+    /**
+     * 成功时返回的列表数量
+     */
+    @JSONField(name = "Count")
+    private Integer count;
+
+    /**
+     * 错误信息
+     */
+    private String respMsg;
+
+    public FeignResponseContent(String respCode, T content, Integer count) {
+        this.respCode = respCode;
+        this.content = content;
+        this.count = count;
+    }
+}

+ 40 - 0
apm-common/src/main/java/com/persagy/apm/common/response/FeignResponseItem.java

@@ -0,0 +1,40 @@
+package com.persagy.apm.common.response;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @description: 服务接口调用返回值
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/9/21 11:01
+ * @version: V1.0
+ */
+@Data
+@NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class FeignResponseItem<T> {
+
+    /**
+     * 响应码
+     */
+    private String respCode;
+
+    /**
+     * 成功时返回的对象数据
+     */
+    @JSONField(name = "Item")
+    private T item;
+
+    /**
+     * 错误信息
+     */
+    private String respMsg;
+
+    public FeignResponseItem(String respCode, T item) {
+        this.respCode = respCode;
+        this.item = item;
+    }
+}

+ 32 - 0
apm-common/src/main/java/com/persagy/apm/common/response/FeignResponseMsg.java

@@ -0,0 +1,32 @@
+package com.persagy.apm.common.response;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @description: 服务接口调用返回值
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/9/21 11:01
+ * @version: V1.0
+ */
+@Data
+@NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class FeignResponseMsg {
+    /**
+     * 响应码
+     */
+    private String respCode;
+
+    /**
+     * 错误信息
+     */
+    private String respMsg;
+
+    public FeignResponseMsg(String respCode, String respMsg) {
+        this.respCode = respCode;
+        this.respMsg = respMsg;
+    }
+}

+ 26 - 0
apm-common/src/main/java/com/persagy/apm/common/response/PageList.java

@@ -0,0 +1,26 @@
+package com.persagy.apm.common.response;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 分页列表
+ * @author Charlie Yu
+ * @data 2021-03-25
+ */
+@Data
+@ApiModel("分页列表")
+public class PageList<T> {
+    @ApiModelProperty("当前页数据")
+    private List<T> records;
+    @ApiModelProperty("总条数")
+    private long total;
+
+    public PageList(final List<T> records, final long total) {
+        this.records = records;
+        this.total = total;
+    }
+}

+ 23 - 0
apm-common/src/main/java/com/persagy/apm/common/serializer/PsDateDeserializer.java

@@ -0,0 +1,23 @@
+package com.persagy.apm.common.serializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.persagy.apm.common.lang.PsDate;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 日期反序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsDateDeserializer extends JsonDeserializer<PsDate> {
+    @Override
+    public PsDate deserialize(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
+        return PsDate.parsePsDate(p.getLongValue());
+    }
+}

+ 22 - 0
apm-common/src/main/java/com/persagy/apm/common/serializer/PsDateSerializer.java

@@ -0,0 +1,22 @@
+package com.persagy.apm.common.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.persagy.apm.common.lang.PsDate;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 日期序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsDateSerializer extends JsonSerializer<PsDate> {
+    @Override
+    public void serialize(PsDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        gen.writeNumber(value.getMillis());
+    }
+}

+ 23 - 0
apm-common/src/main/java/com/persagy/apm/common/serializer/PsDateTimeDeserializer.java

@@ -0,0 +1,23 @@
+package com.persagy.apm.common.serializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.persagy.apm.common.lang.PsDateTime;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 时间反序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsDateTimeDeserializer extends JsonDeserializer<PsDateTime> {
+    @Override
+    public PsDateTime deserialize(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
+        return PsDateTime.parsePsDateTime(p.getLongValue());
+    }
+}

+ 22 - 0
apm-common/src/main/java/com/persagy/apm/common/serializer/PsDateTimeSerializer.java

@@ -0,0 +1,22 @@
+package com.persagy.apm.common.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.persagy.apm.common.lang.PsDateTime;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 时间序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsDateTimeSerializer extends JsonSerializer<PsDateTime> {
+    @Override
+    public void serialize(PsDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        gen.writeNumber(value.getMillis());
+    }
+}

+ 24 - 0
apm-common/src/main/java/com/persagy/apm/common/serializer/PsTimeDeserializer.java

@@ -0,0 +1,24 @@
+package com.persagy.apm.common.serializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.persagy.apm.common.lang.PsTime;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 时间反序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsTimeDeserializer extends JsonDeserializer<PsTime> {
+
+    @Override
+    public PsTime deserialize(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
+        return PsTime.parsePsTime(p.getLongValue());
+    }
+}

+ 22 - 0
apm-common/src/main/java/com/persagy/apm/common/serializer/PsTimeSerializer.java

@@ -0,0 +1,22 @@
+package com.persagy.apm.common.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.persagy.apm.common.lang.PsTime;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 时间序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsTimeSerializer extends JsonSerializer<PsTime> {
+    @Override
+    public void serialize(PsTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        gen.writeNumber(value.getMillis());
+    }
+}

+ 95 - 0
apm-common/src/main/java/com/persagy/apm/common/service/DownloadService.java

@@ -0,0 +1,95 @@
+package com.persagy.apm.common.service;
+
+import com.persagy.apm.common.constant.CommonConstant;
+import com.persagy.apm.common.utils.FileUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.springframework.core.io.Resource;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+/***
+ * Description: 下载Excel的公共逻辑抽象类
+ * @author : lijie
+ * @date :2021/4/28 17:22
+ * Update By lijie 2021/4/28 17:22
+ */
+@Slf4j
+public abstract class DownloadService {
+
+    private static final String CONFIG_PATH_PREFIX="file:./config";
+    private static final String CLASS_PATH_PREFIX="classpath:.";
+
+    /***
+     * Description: 公共下载方法的壳子
+     * @return : void
+     * @author : lijie
+     * @date :2021/4/28 16:55
+     * Update By lijie 2021/4/28 16:55
+     */
+    public void downloadFile() throws IOException {
+        String filePath = getFilePath();
+        Resource[] resolverResources = FileUtil.getResource(CONFIG_PATH_PREFIX+filePath, CLASS_PATH_PREFIX+filePath);
+        if (resolverResources.length<=0){
+            return;
+        }
+        Resource resolverResource = resolverResources[0];
+        String filename = resolverResource.getFilename();
+        InputStream inputStream = resolverResource.getInputStream();
+        Workbook workbook = null;
+        try {
+            workbook = WorkbookFactory.create(inputStream);
+            handleWorkbook(workbook);
+            HttpServletResponse response = getHttpServletResponse();
+            // 为文件命名
+            response.setHeader(CommonConstant.REPONSE_HEAD_CONTENT_DISPOSITION,
+                    CommonConstant.REPONSE_HEAD_CONTENT_DISPOSITION_ATTACHMENT + URLEncoder.encode(filename, "UTF-8"));
+            response.addHeader(CommonConstant.REPONSE_HEAD_CONTENT_TYPE, CommonConstant.REPONSE_HEAD_CONTENT_TYPE_SHEET);
+            workbook.write(response.getOutputStream());
+        } catch (Exception e) {
+            log.error(e.getMessage());
+        } finally {
+            try {
+                inputStream.close();
+            } catch (IOException ignored) {
+                log.error("关闭文件流失败");
+            }
+            if (workbook != null) {
+                try {
+                    workbook.close();
+                } catch (IOException ignored) {
+                    log.error("关闭工作簿失败");
+                }
+            }
+        }
+    }
+    /***
+     * Description: 核心处理业务逻辑的方法
+     * @param workbook : 工作簿
+     * @return : void
+     * @author : lijie
+     * @date :2021/4/28 17:20
+     * Update By lijie 2021/4/28 17:20
+     */
+    protected abstract void handleWorkbook(Workbook workbook);
+    /***
+     * Description: 获取文件路径
+     * @return : java.lang.String
+     * @author : lijie
+     * @date :2021/4/28 17:21
+     * Update By lijie 2021/4/28 17:21
+     */
+    protected abstract String getFilePath();
+    /***
+     * Description: 获取响应对象
+     * @return : javax.servlet.http.HttpServletResponse
+     * @author : lijie
+     * @date :2021/4/28 17:21
+     * Update By lijie 2021/4/28 17:21
+     */
+    protected abstract HttpServletResponse getHttpServletResponse();
+
+}

+ 28 - 0
apm-common/src/main/java/com/persagy/apm/common/utils/DmpExceptionUtil.java

@@ -0,0 +1,28 @@
+package com.persagy.apm.common.utils;
+
+import com.persagy.apm.common.exception.RpcException;
+
+/**
+ * @description: 数据中台异常消息工具类
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/9 9:40 上午
+ * @version: V1.0
+ **/
+public class DmpExceptionUtil {
+
+    /**
+     * @description: 抛出dmp接口调用异常
+     * @param: interfaceName 接口名称
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2021/3/10 5:34 下午
+     * @version: V1.0
+     */
+    public static RpcException rpcException(String interfaceName) {
+        String rpcExceptionMessage = "Dmp%s接口调用异常";
+        return new RpcException(String.format(rpcExceptionMessage, interfaceName));
+    }
+}

+ 34 - 0
apm-common/src/main/java/com/persagy/apm/common/utils/DmpUpsertResultUtil.java

@@ -0,0 +1,34 @@
+package com.persagy.apm.common.utils;
+
+/**
+ * @description:
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/10 11:27 上午
+ * @version: V1.0
+ **/
+public class DmpUpsertResultUtil {
+    /**
+     * @description: 处理数据中台创建或更新结果
+     * @param: dmpUpsertResult
+     * @param: id
+     * @param: dmpInterfaceName
+     * @return: com.persagy.apm.common.response.apmResponseItem<com.persagy.apm.person.model.dto.department.ResponseDepartmentItemVO>
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2021/3/10 11:26 上午
+     * @version: V1.0
+     */
+//    public static apmResponseItem<apmResponseUpsertVO> dealDmpUpsertResult(
+//            String dmpUpsertResult, Long id, String dmpInterfaceName) {
+//        // 处理数据中台返回结果
+//        if (apmDmpResponseUtil.isSuccess(dmpUpsertResult)) {
+//            apmResponseUpsertVO apmResponseUpsertVO = new apmResponseUpsertVO();
+//            apmResponseUpsertVO.setId(id);
+//            return apmResponseUtil.successItem(apmResponseUpsertVO);
+//        } else {
+//            throw DmpExceptionUtil.rpcException(dmpInterfaceName);
+//        }
+//    }
+}

+ 119 - 0
apm-common/src/main/java/com/persagy/apm/common/utils/FileUtil.java

@@ -0,0 +1,119 @@
+package com.persagy.apm.common.utils;
+
+import cn.hutool.http.useragent.OS;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import com.persagy.apm.common.constant.CommonConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.List;
+
+/***
+ * Description: 文件处理公共类
+ * @author : lijie
+ * @date :2021/4/28 16:38
+ * Update By lijie 2021/4/28 16:38
+ */
+@Slf4j
+public class FileUtil {
+
+    /***
+     * Description: 获取资源文件方法
+     * @param configFilePath : 配置文件路径
+     * @param classpathFilePath : 本地路径
+     * @return : org.springframework.core.io.Resource[]
+     * @author : lijie
+     * @date :2021/3/1 14:25
+     * Update By lijie 2021/3/1 14:25
+     */
+    public static Resource[] getResource(String configFilePath, String classpathFilePath) throws IOException {
+        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
+        Resource[] resolverResources = resolver.getResources(configFilePath);
+        if (resolverResources.length > 0) {
+            boolean exist = true;
+            for (Resource resource : resolverResources) {
+                if (!resource.exists()) {
+                    exist = false;
+                    break;
+                }
+            }
+            if(exist) {
+                return resolverResources;
+            }
+        }
+        return resolver.getResources(classpathFilePath);
+    }
+
+    /***
+     * Description: 写错误提示语到文件中
+     * @param errorList : 错误提示语列表
+     * @param response : 响应体对象
+     * @author : lijie
+     * @date :2021/3/4 15:33
+     * Update By lijie 2021/3/4 15:33
+     */
+    public static void writeErrorContentToResponse(List<String> errorList,
+                                            HttpServletResponse response,
+                                            HttpServletRequest request,String errorFileName){
+        try {
+            ServletOutputStream outputStream = response.getOutputStream();
+            String lineSeparator = getSystemLineSeparator(request);
+            for (String error : errorList) {
+                outputStream.write(error.getBytes());
+                outputStream.write(lineSeparator.getBytes());
+            }
+            // 为文件命名
+            response.setHeader(CommonConstant.REPONSE_HEAD_CONTENT_DISPOSITION,
+                    "attachment;filename=" + URLEncoder.encode(errorFileName, "UTF-8"));
+            response.addHeader(CommonConstant.REPONSE_HEAD_CONTENT_TYPE, "text/plain");
+            response.addHeader(CommonConstant.UPLOAD_FLAG,"0");
+            response.addHeader(CommonConstant.REPONSE_HEAD_ACCESS_CONTROL_EXPOSE_HEADERS,CommonConstant.UPLOAD_FLAG);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+        }
+    }
+
+    /***
+     * Description: 获得请求的换行符
+     * @param request : 请求体
+     * @return : void
+     * @author : lijie
+     * @date :2021/3/13 10:57
+     * Update By lijie 2021/3/13 10:57
+     */
+    private static String getSystemLineSeparator(HttpServletRequest request) {
+        String userAgent = request.getHeader(CommonConstant.OS_TYPE);
+        if (StringUtils.isBlank(userAgent)){
+            return System.lineSeparator();
+        }
+        UserAgent agent = UserAgentUtil.parse(userAgent);
+        OS os = agent.getOs();
+        if (null==os){
+            return System.lineSeparator();
+        }
+        String osStr = agent.getOs().toString().toLowerCase();
+        if (StringUtils.isBlank(osStr)){
+            return System.lineSeparator();
+        }
+        if (osStr.contains(CommonConstant.SYSTEM_TYPE_WINDOWS)){
+            return "\r\n";
+        }
+        if (osStr.contains(CommonConstant.SYSTEM_TYPE_LINUX)){
+            return "\r";
+        }
+        if (osStr.contains(CommonConstant.SYSTEM_TYPE_MAC)){
+            return "\n";
+        }
+        return System.lineSeparator();
+    }
+
+}

+ 78 - 0
apm-common/src/main/java/com/persagy/apm/common/utils/ListUtil.java

@@ -0,0 +1,78 @@
+package com.persagy.apm.common.utils;
+
+import org.apache.poi.ss.formula.functions.T;
+import org.assertj.core.util.Lists;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * list工具类
+ *
+ * @author lixing
+ * @version V1.0 2021/4/6 10:52 上午
+ **/
+public class ListUtil {
+    /**
+     * 获取listAfter对比listBefore新增的元素
+     * 使用遍历进行对比,性能有待提升,不适合大数据量对比
+     *
+     * @param listBefore listBefore
+     * @param listAfter listAfter
+     * @return 新增的元素
+     * @author lixing
+     * @version V1.0 2021/4/6 10:58 上午
+     */
+    public static <T> List<T> getAddedList(List<T> listBefore, List<T> listAfter) {
+        if (CollectionUtils.isEmpty(listAfter)) {
+            return null;
+        }
+        if (CollectionUtils.isEmpty(listBefore)) {
+            return listAfter;
+        }
+
+        List<T> result = Lists.newArrayList();
+        listAfter.forEach(t -> {
+            // 如果listAfter中的元素不在listBefore中,认为是新增的元素
+            if (!listBefore.contains(t)) {
+                result.add(t);
+            }
+        });
+        return result;
+    }
+
+    /**
+     * 获取listAfter对比listBefore删除的元素
+     * 使用遍历进行对比,性能有待提升,不适合大数据量对比
+     *
+     * @param listBefore listBefore
+     * @param listAfter listAfter
+     * @return 删除的元素
+     * @author lixing
+     * @version V1.0 2021/4/6 10:58 上午
+     */
+    public static <T> List<T> getDeletedList(List<T> listBefore, List<T> listAfter) {
+        return getAddedList(listAfter, listBefore);
+    }
+
+    public static <T> Boolean listChanged(List<T> listBefore, List<T> listAfter) {
+        if (listBefore == null) {
+            return listAfter != null;
+        }
+        if (listAfter == null) {
+            return true;
+        }
+
+        // 先将list复制再比较,retainAll方法会改变list的内容
+        List<T> listBeforeCopy = Lists.newArrayList(listBefore);
+        List<T> listAfterCopy = Lists.newArrayList(listAfter);
+        return listBeforeCopy.retainAll(listAfterCopy) || listAfterCopy.retainAll(listBeforeCopy);
+    }
+
+//    public static void main(String[] args) {
+//        ArrayList<String> list1 = Lists.newArrayList("1", "2", "3");
+//        ArrayList<String> list2 = Lists.newArrayList("1", "2", "3");
+//        System.out.println(listChanged(list1, list2));
+//    }
+}

+ 127 - 0
apm-common/src/main/java/com/persagy/apm/common/utils/ProxyBeanUtil.java

@@ -0,0 +1,127 @@
+package com.persagy.apm.common.utils;
+
+import org.springframework.cglib.beans.BeanGenerator;
+import org.springframework.cglib.beans.BeanMap;
+
+import java.util.Map;
+
+/**
+ * 动态bean处理工具类
+ * @author Charlie Yu
+ * @version 1.0 2021-03-03
+ */
+public class ProxyBeanUtil {
+
+    /**
+     * 创建Bean生成器
+     * @param source 原始Bean
+     * @param properties 附加属性
+     * @return Bean生成器
+     */
+    public static BeanGenerator createBeanGenerator(Object source, String... properties) {
+        BeanMap sourceMap = BeanMap.create(source);
+        return createBeanGenerator(sourceMap,properties);
+    }
+
+    /**
+     * 创建Bean生成器
+     * @param source 原始Bean
+     * @param propertyMap 附加属性
+     * @return Bean生成器
+     */
+    public static BeanGenerator createBeanGenerator(Object source, Map<String, Class<?>> propertyMap) {
+        BeanMap sourceMap = BeanMap.create(source);
+        return createBeanGenerator(sourceMap,propertyMap);
+    }
+
+    /**
+     * 创建Bean生成器
+     * @param sourceMap 原始Bean对应beanMap
+     * @param properties 附加属性
+     * @return Bean生成器
+     */
+    public static BeanGenerator  createBeanGenerator(BeanMap sourceMap, String... properties) {
+        BeanGenerator generator = new BeanGenerator();
+        generator.setSuperclass(sourceMap.getBean().getClass());
+        int count = 0;
+        for (String prop : properties) {
+            if(sourceMap.containsKey(prop)) {
+                continue;
+            }
+            generator.addProperty(prop,String.class);
+            count++;
+        }
+        if(count  == 0) {
+            return null;
+        }
+        return generator;
+    }
+
+    /**
+     * 创建Bean生成器
+     * @param sourceMap 原始Bean对应beanMap
+     * @param propertyMap 附加属性
+     * @return Bean生成器
+     */
+    public static BeanGenerator  createBeanGenerator(BeanMap sourceMap, Map<String, Class<?>> propertyMap) {
+        BeanGenerator generator = new BeanGenerator();
+        generator.setSuperclass(sourceMap.getBean().getClass());
+        int count = 0;
+        for (Map.Entry<String,Class<?>> entry:propertyMap.entrySet()) {
+            String key = entry.getKey();
+            if (sourceMap.containsKey(key)) {
+                continue;
+            }
+            generator.addProperty(key, entry.getValue());
+            count++;
+        }
+        if(count  == 0) {
+            return null;
+        }
+        return generator;
+    }
+
+    /**
+     * 创建继承于原始Bean的新对象
+     * @param source 原始Bean
+     * @param properties 附加属性
+     * @return 新对象
+     */
+    public static <X> X  createProxyBean(X source, String... properties) {
+        BeanGenerator generator = createBeanGenerator(source,properties);
+        if(generator == null) {
+            return source;
+        }
+        return createProxyBean(generator,source);
+    }
+
+    /**
+     * 创建继承于原始Bean的新对象
+     * @param source 原始Bean
+     * @param propertyMap 附加属性
+     * @return 新对象
+     */
+    public static <X> X  createProxyBean(X source, Map<String, Class<?>> propertyMap) {
+        BeanGenerator generator = createBeanGenerator(source,propertyMap);
+        if(generator == null) {
+            return source;
+        }
+        return createProxyBean(generator,source);
+    }
+
+    /**
+     * 抽取的公共方法
+     * @param generator 新的bean生成器
+     * @param source 复制扩展源对象
+     * @param <X> 源类型
+     * @return 新对象
+     */
+    private static <X> X createProxyBean(BeanGenerator generator,X source){
+        BeanMap sourceMap = BeanMap.create(source);
+        @SuppressWarnings("unchecked")
+        X target = (X) generator.create();
+        BeanMap targetMap = BeanMap.create(target);
+        targetMap.putAll(sourceMap);
+        return target;
+    }
+}

+ 97 - 0
apm-common/src/main/java/com/persagy/apm/common/utils/ResultHelper.java

@@ -0,0 +1,97 @@
+package com.persagy.apm.common.utils;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.persagy.apm.common.response.CommonResult;
+import com.persagy.apm.common.response.PageList;
+import com.persagy.common.enums.ResponseCode;
+import org.apache.commons.collections.CollectionUtils;
+
+import java.util.List;
+
+/**
+ * 响应结果包装助手
+ * @author Charlie Yu
+ * @date 2021-03-25
+ */
+public class ResultHelper {
+
+    /**
+     * 成功消息
+     * @return
+     */
+    public static CommonResult success(){
+        return new CommonResult(ResponseCode.A00000.getCode(), ResponseCode.A00000.getDesc());
+    }
+
+    /**
+     * 成功消息
+     * @return
+     */
+    public static CommonResult success(String respMsg){
+        return new CommonResult(ResponseCode.A00000.getCode(), respMsg);
+    }
+
+    /**
+     * 失败消息
+     * @param respCode
+     * @param respMsg
+     * @return
+     */
+    public static CommonResult failure(String respCode, String respMsg) {
+        return new CommonResult(respCode, respMsg);
+    }
+
+    /**
+     * 单个数据
+     * @param content
+     * @param <T>
+     * @return
+     */
+    public static <T> CommonResult<T> single(T content) {
+        return new CommonResult(ResponseCode.A00000.getCode(), ResponseCode.A00000.getDesc(), content);
+    }
+
+    /**
+     * 单个数据
+     * @param content
+     * @param <T>
+     * @return
+     */
+    public static <T> CommonResult<T> single(T content, String desc) {
+        return new CommonResult(ResponseCode.A00000.getCode(), desc, content);
+    }
+
+    /**
+     * 多个数据 - 列表
+     * @param records
+     * @param <T>
+     * @return
+     */
+    public static <T> CommonResult<PageList<T>> multi(List<T> records) {
+        return multi(records, CollectionUtils.size(records));
+    }
+
+    /**
+     * 多个数据 - 分页
+     * @param page
+     * @param <T>
+     * @return
+     */
+    public static <T> CommonResult<PageList<T>> multi(IPage<T> page) {
+        List<T> records = page == null ? null : page.getRecords();
+        long total = page == null ? 0 : page.getTotal();
+        return multi(records, total);
+    }
+
+    /**
+     * 多个数据
+     * @param records
+     * @param total
+     * @param <T>
+     * @return
+     */
+    private static <T> CommonResult<PageList<T>> multi(List<T> records, long total) {
+        PageList<T> pageList = new PageList<>(records, total);
+        return new CommonResult(ResponseCode.A00000.getCode(), ResponseCode.A00000.getDesc(), pageList);
+    }
+}

+ 144 - 0
apm-common/src/main/java/com/persagy/apm/common/utils/SecureAES.java

@@ -0,0 +1,144 @@
+package com.persagy.apm.common.utils;
+
+import cn.hutool.crypto.Mode;
+import cn.hutool.crypto.Padding;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.symmetric.AES;
+import com.alibaba.fastjson.JSONObject;
+import com.persagy.common.utils.StringUtil;
+import com.persagy.security.exception.AESDecryptException;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 借助于hutool工具类实现 AES 128/256加密,全局编码UTF-8
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-03-13 15:29:50
+ */
+public class SecureAES {
+	
+	public static void main(String[] args) throws UnsupportedEncodingException {
+		SecureAES aes = new SecureAES("63499E35378AE1B0733E3FED7F780B68", "C0E7BD39B52A15C7");
+		JSONObject object = new JSONObject();
+		object.put("groupCode", "TEST");
+		String encryptAccount = aes.encryptAccount(object);
+		System.out.println("account info encrypt: " + encryptAccount);
+		JSONObject decrypt = aes.decryptToken(encryptAccount + ".FSD45SSD1B5D56GB5DFBD");
+		System.out.println("header token decrypt: " + decrypt.toJSONString());
+		JSONObject account = aes.decryptAccount(encryptAccount);
+		System.out.println("account info decrypt: " + account.toJSONString());
+	}
+	
+	private AES aes;
+	
+	public static final Charset CHARSET_UTF_8 = StandardCharsets.UTF_8;
+	
+	/**
+	 * 默认AES/ECB/PKCS5Padding
+	 * 
+	 * @param key 128或256位
+	 */
+	public SecureAES (String key) {
+		aes = SecureUtil.aes(key.getBytes(CHARSET_UTF_8));
+	}
+	
+	/**
+	 * 默认AES/CTS/PKCS5Padding
+	 * 
+	 * @param key 128或256位
+	 * @param iv 16字节
+	 */
+	public SecureAES (String key, String iv) {
+		aes = new AES(Mode.CTS, Padding.PKCS5Padding, key.getBytes(CHARSET_UTF_8), iv.getBytes(CHARSET_UTF_8));
+	}
+	
+	/**
+	 * 加密为16进制
+	 * 
+	 * @param content
+	 * @return
+	 */
+	public String encryptHex(String content) {
+		return aes.encryptHex(content, CHARSET_UTF_8);
+	}
+	
+	/**
+	 * 解密
+	 * 
+	 * @param content
+	 * @return
+	 */
+	public String decryptStr(String content) {
+		return aes.decryptStr(content, CHARSET_UTF_8);
+	}
+	
+	/**
+	 * 解密
+	 * 
+	 * @param content
+	 * @return
+	 * @throws UnsupportedEncodingException 
+	 */
+	public String decryptFromBase64(String content) throws UnsupportedEncodingException {
+		String decode = URLDecoder.decode(content, CHARSET_UTF_8.toString());
+		return aes.decryptStr(decode, CHARSET_UTF_8);
+	}
+	
+	/**
+	 * 加密为16进制,参数组装为json格式数据
+	 */
+	public String encryptToBase64(JSONObject object) throws UnsupportedEncodingException {
+		String encryptHex = aes.encryptHex(object.toJSONString(), CHARSET_UTF_8);
+		return URLEncoder.encode(encryptHex, CHARSET_UTF_8.toString());
+	}
+	
+	/**
+	 * 加密为16进制,参数组装为json格式数据,且经过URLEncoder编码
+	 * 
+	 * @throws UnsupportedEncodingException
+	 */
+	public String encryptAccount(JSONObject object) throws UnsupportedEncodingException {
+		String encryptHex = aes.encryptHex(object.toJSONString(), CHARSET_UTF_8);
+		return URLEncoder.encode(encryptHex, CHARSET_UTF_8.toString());
+	}
+	
+	/**
+	 * 解密,且经过URLDecoder编码
+	 * 
+	 * @return
+	 * @throws UnsupportedEncodingException 
+	 */
+	public JSONObject decryptToken(String headerToken) throws UnsupportedEncodingException {
+		if (StringUtil.isBlank(headerToken)) {
+			throw new AESDecryptException("token is null");
+		}
+		String[] tokens = headerToken.split("\\.");
+		if (tokens.length < 2) {
+			throw new AESDecryptException("token invalid parameter");
+		}
+		String encryptAccount = tokens[0];			// 加密的账号信息
+		return this.decryptAccount(encryptAccount);
+	}
+	
+	/**
+	 * 解密
+	 * 
+	 * @throws UnsupportedEncodingException
+	 */
+	public JSONObject decryptAccount(String encryptAccount) throws UnsupportedEncodingException {
+		String decryptStr = this.decryptFromBase64(encryptAccount);
+		if (StringUtil.isBlank(decryptStr)) {
+			throw new AESDecryptException("AES decrypt failure");
+		}
+		JSONObject object = JSONObject.parseObject(decryptStr);
+		return object;
+	}
+	
+}

+ 338 - 0
apm-common/src/main/java/com/persagy/apm/common/utils/TypeConvertUtil.java

@@ -0,0 +1,338 @@
+package com.persagy.apm.common.utils;
+
+import com.persagy.apm.common.model.data.DataType;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 数据类型转换器
+ * @author Charlie Yu
+ * @version 1.0 2021-03-03
+ */
+@Slf4j
+public class TypeConvertUtil {
+
+    /**
+     * 私有构造方法,防止实例化
+     */
+    private TypeConvertUtil() {
+
+    }
+
+    /**
+     * 将Object对象解析成字符串类型
+     * @param obj 需要转换的对象
+     * @return 字符串
+     */
+    public static final String parseString(Object obj) {
+        if (obj == null) {
+            return null;
+        }
+        return obj.toString();
+    }
+
+    /**
+     * 将对象解析成字节类型
+     * @param obj 需要转换的对象
+     * @return 字节值
+     */
+    public static final byte parseByte(Object obj) {
+        if (obj == null) {
+            return 0;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).byteValue();
+        }
+        if (obj instanceof Boolean) {
+            return (byte) (((Boolean) obj).booleanValue() ? 1 : 0);
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0;
+        } else {
+            return Byte.parseByte(s);
+        }
+    }
+
+    /**
+     * 将对象解析成字节类型
+     * @param obj 需要转换的对象
+     * @return 字节值
+     */
+    public static final short parseShort(Object obj) {
+        if (obj == null) {
+            return 0;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).shortValue();
+        }
+        if (obj instanceof Boolean) {
+            return (short) (((Boolean) obj).booleanValue() ? 1 : 0);
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0;
+        } else {
+            return Short.parseShort(s);
+        }
+    }
+
+    /**
+     * 将对象解析成整数类型
+     * @param obj 需转换的对象
+     * @return 整数值
+     */
+    public static final int parseInt(Object obj) {
+        if (obj == null) {
+            return 0;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).intValue();
+        }
+        if (obj instanceof Boolean) {
+            return !((Boolean) obj).booleanValue() ? 0 : 1;
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0;
+        } else {
+            return Integer.parseInt(s);
+        }
+    }
+
+    /**
+     * 将对象解析成长整类型
+     * @param obj 需转换的对象
+     * @return 长整数值
+     */
+    public static final long parseLong(Object obj) {
+        if (obj == null) {
+            return 0;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).longValue();
+        }
+        if (obj instanceof Boolean) {
+            return !((Boolean) obj).booleanValue() ? 0L : 1L;
+        }
+        if (obj instanceof Date) {
+            return ((Date) obj).getTime();
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0;
+        } else {
+            return Long.parseLong(s);
+        }
+    }
+
+    /**
+     * 将对象解析成布尔类型数据
+     * @param obj 需转换的对象
+     * @return 布尔值
+     */
+    public static final boolean parseBoolean(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (obj instanceof Boolean) {
+            return ((Boolean) obj).booleanValue();
+        } else {
+            String s = obj.toString();
+            return "true".equalsIgnoreCase(s) || "1".equals(s) || "Y".equalsIgnoreCase(s) || "YES".equalsIgnoreCase(s);
+        }
+    }
+
+    /**
+     * 将对象解析成浮点型数据
+     * @param obj 需转换的对象
+     * @return 浮点数值
+     */
+    public static final float parseFloat(Object obj) {
+        if (obj == null) {
+            return 0.0f;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).floatValue();
+        }
+        if (obj instanceof Boolean) {
+            return !((Boolean) obj).booleanValue() ? 0.0F : 1.0F;
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0.0f;
+        } else {
+            return Float.parseFloat(s);
+        }
+    }
+
+    /**
+     * 将对象解析成双浮点型数据
+     * @param obj 需转换的对象
+     * @return 双浮点值
+     */
+    public static final double parseDouble(Object obj) {
+        if (obj == null) {
+            return 0.0d;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).doubleValue();
+        }
+        if (obj instanceof Boolean) {
+            return !((Boolean) obj).booleanValue() ? 0.0D : 1.0D;
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0.0d;
+        } else {
+            return Double.parseDouble(s);
+        }
+    }
+
+    /**
+     * 解析对象成大数字类型数据
+     * @param obj 需要转换的对象
+     * @return 大数字对象
+     */
+    public static final BigDecimal parseBigDecimal(Object obj) {
+        if (obj == null) {
+            return BigDecimal.valueOf(0L);
+        }
+        if (obj instanceof BigDecimal) {
+            return (BigDecimal) obj;
+        }
+        if (obj instanceof Number) {
+            return BigDecimal.valueOf(((Number) obj).doubleValue());
+        }
+        if (obj instanceof Boolean) {
+            return BigDecimal.valueOf(((Boolean) obj).booleanValue() ? 1L : 0L);
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return BigDecimal.valueOf(0L);
+        } else {
+            return new BigDecimal(s);
+        }
+    }
+
+    /**
+     * 解析对象成日期数据类型
+     *
+     * @param obj 需转换的对象
+     * @return 日期对象
+     */
+    public static Date parseDate(Object obj) {
+        if (obj == null) {
+            return null;
+        }
+        if (obj instanceof Date) {
+            return (Date) obj;
+        }
+        if (obj instanceof Number) {
+            return new Date(((Number) obj).longValue());
+        }
+        String objString = String.valueOf(obj);
+        if (StringUtils.isBlank(objString)) {
+            return null;
+        }
+        if (isNumeric(objString)) {
+            long time = Long.parseLong(objString);
+            return new Date(time);
+        }
+        int len = objString.length();
+        if (len >= 19) {
+            return null;
+        }
+        boolean isDate = objString.indexOf("-") > 0;
+        boolean isTime = objString.indexOf(":") > 0;
+        try {
+            if (isDate && isTime) {
+                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(objString);
+            }
+            if (isDate) {
+                return new SimpleDateFormat("yyyy-MM-dd").parse(objString);
+            }
+            if (isTime) {
+                return new SimpleDateFormat("HH:mm:ss").parse(objString);
+            }
+        } catch (ParseException e) {
+            log.error("TypeConvert parse: Date parse error", e);
+        }
+        return null;
+    }
+
+    /**
+     * 将未知类型的对象转换成指定类型的对象
+     * @param clazz 数据类型
+     * @param obj  未知类型对象
+     * @return 目标类型对象
+     */
+    public static final Object translate(Class clazz, Object obj) {
+        return translate(DataType.parse(clazz), obj);
+    }
+
+    /**
+     * 将未知类型的对象转换成指定类型的对象
+     * @param dataType 数据类型
+     * @param obj      未知类型对象
+     * @return 目标类型对象
+     */
+    public static final Object translate(DataType dataType, Object obj) {
+        boolean isBlank = obj == null || (obj instanceof String) && ((String) obj).length() == 0;
+        if (isBlank) {
+            if (dataType == DataType.STRING) {
+                return obj;
+            } else {
+                return null;
+            }
+        }
+        switch (dataType) {
+            case STRING:
+                return parseString(obj);
+            case INT:
+                return parseInt(obj);
+            case LONG:
+                return parseLong(obj);
+            case BIGDECIMAL:
+                return parseBigDecimal(obj);
+            case BOOLEAN:
+                return parseBoolean(obj);
+            case FLOAT:
+                return parseFloat(obj);
+            case DOUBLE:
+                return parseDouble(obj);
+            case DATE:
+            case TIME:
+            case DATETIME:
+                return parseDate(obj);
+            case BYTE:
+                return parseByte(obj);
+            case SHORT:
+                return parseShort(obj);
+            default:
+                return obj;
+        }
+    }
+
+    /**
+     * 判断字符串是否是有效的数组
+     * @param s 需要判断的字符串
+     * @return 布尔值
+     */
+    public static boolean isNumeric(String s) {
+        int length = s.length();
+        for (int i = 0; i < length; i++) {
+            char c = s.charAt(i);
+            boolean isNumberChar = (c < '0' || c > '9') && c != '.' && (i != 0 || c != '-');
+            if (isNumberChar) {
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 12 - 0
apm-common/src/main/java/com/persagy/apm/common/validator/Delete.java

@@ -0,0 +1,12 @@
+package com.persagy.apm.common.validator;
+
+import javax.validation.groups.Default;
+
+/***
+ * Description: 集团SOP删除的验证接口
+ * @author : lijie
+ * @date :2021/4/10 17:58
+ * Update By lijie 2021/4/10 17:58
+ */
+public interface Delete extends Default {
+}

+ 12 - 0
apm-common/src/main/java/com/persagy/apm/common/validator/Insert.java

@@ -0,0 +1,12 @@
+package com.persagy.apm.common.validator;
+
+import javax.validation.groups.Default;
+
+/***
+ * Description: 集团SOP插入的校验器
+ * @author : lijie
+ * @date :2021/4/2 12:07
+ * Update By lijie 2021/4/2 12:07
+ */
+public interface Insert extends Default {
+}

+ 12 - 0
apm-common/src/main/java/com/persagy/apm/common/validator/Query.java

@@ -0,0 +1,12 @@
+package com.persagy.apm.common.validator;
+
+import javax.validation.groups.Default;
+
+/***
+ * Description: 集团SOP的校验器
+ * @author : lijie
+ * @date :2021/4/10 17:59
+ * Update By lijie 2021/4/10 17:59
+ */
+public interface Query extends Default {
+}

+ 12 - 0
apm-common/src/main/java/com/persagy/apm/common/validator/Update.java

@@ -0,0 +1,12 @@
+package com.persagy.apm.common.validator;
+
+import javax.validation.groups.Default;
+
+/***
+ * Description: 更新的校验器
+ * @author : lijie
+ * @date :2021/4/10 18:00
+ * Update By lijie 2021/4/10 18:00
+ */
+public interface Update extends Default {
+}

+ 176 - 0
apm-mybatis/README.md

@@ -0,0 +1,176 @@
+apm-mybatis 关系数据库
+============ 
+mybatis-plus封装
+- 支持多数据源自动切换
+- 支持预置建库脚本
+- 提供树型内部码生成工具
+
+说明
+---------------
+多数据源
+* 配置文件参考:com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties
+* [配置文件示例](https://github.com/dynamic-datasource/dynamic-datasource-doc/blob/master/docs/guide/integration/Druid.md)
+```
+spring:
+  datasource:
+    druid:
+      stat-view-servlet:
+        enabled: true
+        loginUsername: admin
+        loginPassword: 123456
+    dynamic:
+      druid: #以下是支持的全局默认值
+        initial-size:
+        max-active:
+        min-idle:
+        max-wait:
+        time-between-eviction-runs-millis:
+        time-between-log-stats-millis:
+        stat-sqlmax-size:
+        min-evictable-idle-time-millis:
+        max-evictable-idle-time-millis:
+        test-while-idle:
+        test-on-borrow:
+        test-on-return:
+        validation-query:
+        validation-query-timeout:
+        use-global-datasource-stat:
+        async-init:
+        clear-filters-enable:
+        reset-stat-enable:
+        not-full-timeout-retry-count:
+        max-wait-thread-count:
+        fail-fast:
+        phyTimeout-millis:
+        keep-alive:
+        pool-prepared-statements:
+        init-variants:
+        init-global-variants:
+        use-unfair-lock:
+        kill-when-socket-read-timeout:
+        connection-properties:
+        max-pool-prepared-statement-per-connection-size:
+        init-connection-sqls:
+        share-prepared-statements:
+        connection-errorretry-attempts:
+        break-after-acquire-failure:
+        filters: stat,wall # 注意这个值和druid原生不一致,默认启动了stat,wall
+        wall:
+            noneBaseStatementAllow:
+            callAllow:
+            selectAllow:
+            selectIntoAllow:
+            selectIntoOutfileAllow:
+            selectWhereAlwayTrueCheck:
+            selectHavingAlwayTrueCheck:
+            selectUnionCheck:
+            selectMinusCheck:
+            selectExceptCheck:
+            selectIntersectCheck:
+            createTableAllow:
+            dropTableAllow:
+            alterTableAllow:
+            renameTableAllow:
+            hintAllow:
+            lockTableAllow:
+            startTransactionAllow:
+            blockAllow:
+            conditionAndAlwayTrueAllow:
+            conditionAndAlwayFalseAllow:
+            conditionDoubleConstAllow:
+            conditionLikeTrueAllow:
+            selectAllColumnAllow:
+            deleteAllow:
+            deleteWhereAlwayTrueCheck:
+            deleteWhereNoneCheck:
+            updateAllow:
+            updateWhereAlayTrueCheck:
+            updateWhereNoneCheck:
+            insertAllow:
+            mergeAllow:
+            minusAllow:
+            intersectAllow:
+            replaceAllow:
+            setAllow:
+            commitAllow:
+            rollbackAllow:
+            useAllow:
+            multiStatementAllow:
+            truncateAllow:
+            commentAllow:
+            strictSyntaxCheck:
+            constArithmeticAllow:
+            limitZeroAllow:
+            describeAllow:
+            showAllow:
+            schemaCheck:
+            tableCheck:
+            functionCheck:
+            objectCheck:
+            variantCheck:
+            mustParameterized:
+            doPrivilegedAllow:
+            dir:
+            tenantTablePattern:
+            tenantColumn:
+            wrapAllow:
+            metadataAllow:
+            conditionOpXorAllow:
+            conditionOpBitwseAllow:
+            caseConditionConstAllow:
+            completeInsertValuesCheck:
+            insertValuesCheckSize:
+            selectLimit:
+        stat:
+          merge-sql:
+          log-slow-sql:
+          slow-sql-millis: 
+      datasource:
+        master:
+          username: root
+          password: 123456
+          driver-class-name: com.mysql.jdbc.Driver
+          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic?characterEncoding=utf8&useSSL=false
+          druid: # 以下是独立参数,每个库可以重新设置
+            initial-size:
+            validation-query: select 1 FROM DUAL #比如oracle就需要重新设置这个
+            public-key: #(非全局参数)设置即表示启用加密,底层会自动帮你配置相关的连接参数和filter,推荐使用本项目自带的加密方法。
+#           ......
+
+# 生成 publickey 和密码,推荐使用本项目自带的加密方法。
+# java -cp druid-1.1.10.jar com.alibaba.druid.filter.config.ConfigTools youpassword
+```
+
+预置建库脚本
+* [参考配置](https://github.com/dynamic-datasource/dynamic-datasource-doc/blob/master/docs/guide/advance/Init-Schema-Data.md)
+* 脚本文件放在resources目录,在默认数据源中增加配置:
+  ```
+  spring:
+    datasource:
+      dynamic:
+        primary: order
+        datasource:
+          order:
+            # 基础配置省略...
+            schema: db/order/schema.sql # 配置则生效,自动初始化表结构。
+            data: db/order/data.sql # 配置则生效,自动初始化数据。
+            continue-on-error: true # 默认true,初始化失败是否继续
+            separator: ";" # sql默认分号分隔符,一般无需更改
+  ```
+* 注意事项:
+  - schema: 建立连接时会执行,因此建表应使用CREATE TABLE IF NOT EXISTS确保不会重复创建。
+  - data: 建立连接时会执行,应确保数据不会重复插入。
+
+树型内部码工具
+* 可对实现ITreeEntity的model自动生成innerCode内部码。
+	
+自定义日期类型转换
+* 全局处理日期类型转换char,需要增加配置
+  ```
+  mybatis-plus:
+    typeHandlersPackage: com.persagy.apm.mybatis.type
+  ```
+
+最新变化
+---------------
+

+ 35 - 0
apm-mybatis/pom.xml

@@ -0,0 +1,35 @@
+<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.persagy</groupId>
+        <artifactId>apm-parent</artifactId>
+        <version>3.0.0</version>
+        <relativePath>../apm-parent</relativePath>
+    </parent>
+    <artifactId>apm-mybatis</artifactId>
+    <distributionManagement>
+        <repository>
+            <id>persagy</id>
+            <name>releases Repository</name>
+            <url>http://47.93.132.139:8081/nexus/content/repositories/persagy/</url>
+        </repository>
+    </distributionManagement>
+    <dependencies>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>apm-common</artifactId>
+        </dependency>
+        <!-- mybatisPlus相关的依赖 start -->
+        <!-- mybatisPlus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+            <version>3.3.2</version>
+        </dependency>
+    </dependencies>
+</project>

+ 16 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/annotations/FormatMybatisQueryParams.java

@@ -0,0 +1,16 @@
+package com.persagy.apm.mybatis.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * mybatis查询参数格式化注解
+ *
+ * @author lixing
+ * @version V1.0 2021/4/23 3:58 下午
+ **/
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Inherited
+@Documented
+public @interface FormatMybatisQueryParams {
+}

+ 68 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/aop/FormatMybatisQueryParamsAspect.java

@@ -0,0 +1,68 @@
+package com.persagy.apm.mybatis.aop;
+
+import com.persagy.apm.mybatis.utils.MyBatisStringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * 格式化mybatis请求参数切面
+ *
+ * @author lixing
+ * @version V1.0 2021/4/23 4:00 下午
+ **/
+@Component
+@Aspect
+@Slf4j
+public class FormatMybatisQueryParamsAspect {
+    @Pointcut("@annotation(com.persagy.apm.mybatis.annotations.FormatMybatisQueryParams)")
+    private void pointCutMethod() {
+    }
+
+    @Before("pointCutMethod()")
+    public void process(JoinPoint point) throws Throwable {
+        // 获取方法的参数
+        Object[] args = point.getArgs();
+        for (Object arg : args) {
+            // 获取方法入参的属性
+            Field[] declaredFields = arg.getClass().getDeclaredFields();
+            for (Field declaredField : declaredFields) {
+                if (declaredField.getType().equals(String.class)) {
+                    Method getMethod = arg.getClass().getDeclaredMethod(
+                            "get" + MyBatisStringUtils.firstCharUpperCase(declaredField.getName()));
+                    String s = (String) getMethod.invoke(arg);
+                    s = formatUnderLine(s);
+                    Method setMethod = arg.getClass().getDeclaredMethod(
+                            "set" + MyBatisStringUtils.firstCharUpperCase(declaredField.getName()), String.class);
+                    setMethod.invoke(arg, s);
+                }
+            }
+        }
+    }
+
+    /**
+     * 格式化下划线,如果字符串中包含下划线,将下划线替换为"\_"
+     * mysql中下划线代表任意字符
+     *
+     * @param s 格式化前的字符串
+     * @return 格式化后的字符串
+     * @author lixing
+     * @version V1.0 2021/4/23 4:14 下午
+     */
+    private String formatUnderLine(String s) {
+        if (StringUtils.isBlank(s)) {
+            return s;
+        }
+        if (s.contains("_")) {
+            s = s.replaceAll("_", "\\\\_");
+        }
+        return s;
+    }
+}

+ 26 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/config/MyBatisWebConfigurer.java

@@ -0,0 +1,26 @@
+package com.persagy.apm.mybatis.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * MyBatis配置
+ * @author Charlie Yu
+ * @date 2021-03-29
+ */
+@Configuration
+@Order(2)
+public class MyBatisWebConfigurer implements WebMvcConfigurer {
+
+//    @Bean
+//    public DynamicDataSourceHandler dynamicDataSourceHandler() {
+//        return new DynamicDataSourceHandler();
+//    }
+//
+//    @Override
+//    public void addInterceptors(InterceptorRegistry registry) {
+//        // 设置拦截的路径、不拦截的路径、优先级等等 -- 在contextHandler之后执行
+//        registry.addInterceptor(dynamicDataSourceHandler()).order(20).addPathPatterns("/**");
+//    }
+}

+ 36 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/config/MybatisPlusConfig.java

@@ -0,0 +1,36 @@
+package com.persagy.apm.mybatis.config;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * <p>
+ * MybatisPlusConfig
+ * </p>
+ *
+ * @author lixing
+ * @description 由 Mybatisplus Code Generator 创建
+ * @since 2020-09-09 09:13:27
+ **/
+@Configuration
+@EnableTransactionManagement
+public class MybatisPlusConfig {
+    /**
+     * @param
+     * @description:配置分页插件 不配置分页插件,MP提供的分页方法会查询全部,分页不生效
+     * 使用实例:
+     * IPage<Member> memberPage = new Page<>(1, 2,false) true查询总记录数量
+     * @exception:
+     * @author: LuoGuangyi
+     * @company: Persagy Technology Co.,Ltd
+     * @return: com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor
+     * @since: 2020/09/30 10:33
+     * @version: V1.0
+     */
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor();
+    }
+}

+ 17 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/dao/DbDao.java

@@ -0,0 +1,17 @@
+package com.persagy.apm.mybatis.dao;
+
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 数据库操作dao
+ * @author Charlie Yu
+ * @date 2021-03-30
+ */
+public interface DbDao {
+
+    /**
+     * 创建schema
+     * @param schema 实例名
+     */
+    void createDataBase(@Param("schema") String schema);
+}

+ 30 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/dao/SimpleTreeCodeDao.java

@@ -0,0 +1,30 @@
+package com.persagy.apm.mybatis.dao;
+
+import org.apache.ibatis.annotations.Param;
+
+
+/**
+ * 树编码使用的dao
+ * @author Charlie Yu
+ * @date 2021-03-29
+ */
+public interface SimpleTreeCodeDao {
+
+    /**
+     * 取得指定表指定树层次的最大编码
+     * @param tableName      表名
+     * @param parentCode     父节点编码
+     * @param innerCodeField 内码字段名
+     * @return 最大编码
+     */
+    String getMaxCode(@Param("tableName") String tableName, @Param("parentCode") String parentCode, @Param("innerCodeField") String innerCodeField);
+
+    /**
+     * 更新指定表的指定上级的实体的树编码
+     * @param tableName     表名
+     * @param parentCode    新的上级编码
+     * @param oldParentCode 旧的上级编码
+     * @param innerCodeField 内码字段名
+     */
+    void updateSubTreeCode(@Param("tableName") String tableName, @Param("parentCode") String parentCode, @Param("oldParentCode") String oldParentCode, @Param("innerCodeField") String innerCodeField);
+}

+ 40 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/handler/CommonMetaObjectHandler.java

@@ -0,0 +1,40 @@
+package com.persagy.apm.mybatis.handler;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.persagy.apm.common.constant.enums.ValidEnum;
+import com.persagy.apm.common.model.entity.AuditableEntity;
+import com.persagy.apm.common.model.entity.BaseEntity;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * @description:
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/9/9 2:10 下午
+ * @version: V1.0
+ **/
+@Component
+public class CommonMetaObjectHandler implements MetaObjectHandler {
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        if (metaObject.hasSetter(AuditableEntity.DO_PROP_CREATIONTIME)) {
+            this.setFieldValByName(AuditableEntity.DO_PROP_CREATIONTIME, new Date(), metaObject);
+        }
+        if (metaObject.hasSetter(AuditableEntity.DO_PROP_MODIFIEDTIME)) {
+            this.setFieldValByName(AuditableEntity.DO_PROP_MODIFIEDTIME, new Date(), metaObject);
+        }
+        if (metaObject.hasSetter(BaseEntity.PROP_VALID)) {
+            this.setFieldValByName(BaseEntity.PROP_VALID, ValidEnum.TRUE.getType(), metaObject);
+        }
+    }
+
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        if (metaObject.hasSetter(AuditableEntity.DO_PROP_MODIFIEDTIME)) {
+            this.setFieldValByName(AuditableEntity.DO_PROP_MODIFIEDTIME, new Date(), metaObject);
+        }
+    }
+}

+ 46 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/handler/DbJsonTypeHandler.java

@@ -0,0 +1,46 @@
+package com.persagy.apm.mybatis.handler;
+
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+import org.springframework.util.Assert;
+/***
+ * Description: 针对mysql中json类型与Model映射处理器.用于处理集合的泛型不是String类型的
+ * @author : lijie
+ * @date :2021/4/27 15:18
+ * Update By lijie 2021/4/27 15:18
+ */
+@Slf4j
+@MappedTypes({ Object.class })
+@MappedJdbcTypes(JdbcType.VARCHAR)
+public class DbJsonTypeHandler extends AbstractJsonTypeHandler<Object> {
+
+    private final Class<?> clazz;
+
+    public DbJsonTypeHandler(Class<?> type, Class<?> innerType) {
+        if (log.isTraceEnabled()) {
+            log.trace("JacksonTypeHandler(" + type + ")");
+        }
+        Assert.notNull(type, "Type argument cannot be null");
+
+        this.clazz = innerType;
+    }
+
+    @Override
+    protected Object parse(String json) {
+        if (JSONUtil.isJsonArray(json)) {
+            return JSONUtil.toList(JSONUtil.parseArray(json), this.clazz);
+        } else {
+            return JSONUtil.toBean(json, this.clazz);
+        }
+    }
+
+    @Override
+    protected String toJson(Object obj) {
+        return JSONUtil.toJsonStr(obj);
+    }
+
+}

+ 92 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/handler/DynamicDataSourceHandler.java

@@ -0,0 +1,92 @@
+package com.persagy.apm.mybatis.handler;
+
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
+import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
+import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
+import com.persagy.apm.common.context.AppContext;
+import com.persagy.apm.common.helper.SpringHelper;
+import com.persagy.apm.mybatis.service.IDbService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.sql.DataSource;
+
+/**
+ * 动态数据源拦截器
+ * @author Charlie Yu
+ * @date 2021-03-29
+ */
+public class DynamicDataSourceHandler extends HandlerInterceptorAdapter {
+
+    /** 忽略的url - swagger的文档不校验 */
+    private static final String[] IGNORE_URL = {".html", ".js", ".css", "/swagger-resources"};
+
+    @Autowired
+    private DynamicRoutingDataSource dataSource;
+    @Autowired
+    private DefaultDataSourceCreator dataSourceCreator;
+    @Autowired
+    private DynamicDataSourceProperties defaultProperty;
+    @Autowired
+    private IDbService dbService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String requestURI = request.getRequestURI();
+        if (StringUtils.containsAny(requestURI, IGNORE_URL)) {
+            return true;
+        }
+        resetDataSource();
+        return true;
+    }
+
+    /**
+     * 多租户处理,数据源切换
+     */
+    public void resetDataSource() {
+        // 是否启用多数据源
+        boolean isDynamic = SpringHelper.getBoolean("spring.datasource.dynamic.enabled", false);
+        if(!isDynamic) {
+            return;
+        }
+        // 默认为应用名
+        String dbNameDefault = SpringHelper.getString("spring.application.name");
+        // 创建的数据源名称: 集团编码_应用名
+        String newDsName = AppContext.getContext().getGroupCode() + "_" + dbNameDefault;
+        // 数据源中是否已存在
+        if(!dataSource.getCurrentDataSources().keySet().contains(newDsName)) {
+            // 设置新数据源
+            DataSourceProperty property = copyProperty(dbNameDefault, newDsName);
+            // 创建数据库实例
+            dbService.createDataBase(newDsName);
+            // 创建数据源
+            DataSource currDs = dataSourceCreator.createDataSource(property);
+            dataSource.addDataSource(newDsName, currDs);
+        }
+        // 设置当前数据源
+        DynamicDataSourceContextHolder.push(newDsName);
+    }
+
+    /**
+     * 复制新的数据源
+     * @param dbNameDefault 默认连接的实例名
+     * @param newDsName 新创建的实例名
+     * @return
+     */
+    private DataSourceProperty copyProperty(String dbNameDefault, String newDsName) {
+        // 取默认数据源
+        DataSourceProperty srcProperty = defaultProperty.getDatasource().get(defaultProperty.getPrimary());
+        // 设置新数据源
+        DataSourceProperty property = new DataSourceProperty();
+        BeanUtils.copyProperties(srcProperty, property);
+        property.setPoolName(newDsName);
+        property.setUrl(StringUtils.replace(property.getUrl(), dbNameDefault, newDsName));
+        return property;
+    }
+}

+ 137 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/helper/TreeCodeHelper.java

@@ -0,0 +1,137 @@
+package com.persagy.apm.mybatis.helper;
+
+import com.persagy.apm.common.helper.SpringHelper;
+import com.persagy.apm.common.model.entity.ITreeEntity;
+import com.persagy.apm.mybatis.service.ITreeCodeService;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * 树编码助手
+ * @author Charlie Yu
+ * @date 2021-03-29
+ */
+public final class TreeCodeHelper {
+
+    /**
+     * 编码引擎
+     */
+    private static ITreeCodeService service = null;
+
+    /**
+     * 取得编码服务
+     * @return 编码服务
+     */
+    public static ITreeCodeService getService() {
+        if (service == null) {
+            service = SpringHelper.getBean(ITreeCodeService.class);
+        }
+        return service;
+    }
+
+    /**
+     * 为指定实体生成树编码
+     * @param entity     树形实体
+     * @param parentCode 上级编码
+     * @return 树形实体
+     */
+    public static <T extends ITreeEntity> T generateTreeCode(T entity, String parentCode) {
+        return getService().generateTreeCode(entity, parentCode);
+    }
+
+    /**
+     * 根据上级实体生成下级编码,如果上级实体为空则传入空实体
+     * @param parent 上级实体
+     * @return 树形编码
+     */
+    public static <T extends ITreeEntity> String generateTreeCodeByParent(T parent) {
+        return getService().generateTreeCodeByParent(parent);
+    }
+
+    /**
+     * 根据表名上级节点id上级节点编码生成新的下级编码
+     * @param tableName      表名
+     * @param parentCode     上级节点编码
+     * @param extendWhereSql 扩展条件
+     * @return 新的编码
+     */
+    public static String generateTreeCode(String tableName, String parentCode, String extendWhereSql) {
+        return getService().generateTreeCode(tableName, parentCode, extendWhereSql);
+    }
+
+    /**
+     * 为一组相同上级的实体生成树编码
+     * @param entitys    树形实体数组
+     * @param parentCode 上级编码
+     * @return 树形实体数组
+     */
+    public static <T extends ITreeEntity> List<T> generateTreeCodes(List<T> entitys, String parentCode) {
+        return getService().generateTreeCodes(entitys, parentCode);
+    }
+
+    /**
+     * 根据上级实体生成多个下级编码,如果上级实体为空则传入空实体
+     * @param parent 上级实体
+     * @param length 编码个数
+     * @return 编码数组
+     */
+    public static <T extends ITreeEntity> String[] generateTreeCodesByParent(T parent, int length) {
+        return getService().generateTreeCodesByParent(parent, length);
+    }
+
+    /**
+     * 根据实体树编码同步其所有子节点编码
+     * @param entity      需要同步子节点编码的节点
+     * @param oldTreeCode 原有的实体树编码
+     */
+    public static <T extends ITreeEntity> void synchronizeSubTree(T entity, String oldTreeCode) {
+        getService().synchronizeSubTree(entity, oldTreeCode);
+    }
+
+    /**
+     * 树形实体更改上级后的处理
+     * @param entity     树形实体
+     * @param parentCode 上级编码
+     */
+    public static <T extends ITreeEntity> ITreeEntity afterMoveNodeToParent(T entity, String parentCode) {
+        return getService().afterMoveNodeToParent(entity, parentCode);
+    }
+
+    /**
+     * 取得当前编码的所有上级节点编码数组
+     * @param treeCode 树编码
+     * @return 上级节点编码数组
+     */
+    public static String[] getSuppriorTreeCodes(String treeCode, boolean includeSelf) {
+        if (StringUtils.isBlank(treeCode) || treeCode.length() == ITreeCodeService.TREE_CODE_LENGTH) {
+            if (treeCode != null && treeCode.length() == ITreeCodeService.TREE_CODE_LENGTH && includeSelf) {
+                return new String[]{treeCode};
+            }
+            return null;
+        }
+        if (treeCode.length() % ITreeCodeService.TREE_CODE_LENGTH != 0) {
+            throw new IllegalArgumentException();
+        }
+
+        List<String> parentCodes = new ArrayList<String>();
+        for (int i = ITreeCodeService.TREE_CODE_LENGTH; i < treeCode.length(); i += ITreeCodeService.TREE_CODE_LENGTH) {
+            String code = treeCode.substring(0, i);
+            parentCodes.add(code);
+        }
+        if (includeSelf) {
+            parentCodes.add(treeCode);
+        }
+        return parentCodes.toArray(new String[parentCodes.size()]);
+    }
+
+    public static void main(String[] args) {
+        Arrays.stream(getSuppriorTreeCodes("00001", true)).forEach(s -> {
+            System.out.println(s);
+        });
+    }
+
+}

+ 0 - 0
apm-mybatis/src/main/java/com/persagy/apm/mybatis/service/IDbService.java


Някои файлове не бяха показани, защото твърде много файлове са промени