liukx@elab 5 лет назад
Родитель
Сommit
7ef98f389e
100 измененных файлов с 3869 добавлено и 139 удалено
  1. 1 1
      elab-alert/pom.xml
  2. 1 1
      elab-annotation/pom.xml
  3. 3 3
      elab-cache/pom.xml
  4. 3 3
      elab-core/pom.xml
  5. 2 2
      elab-core/src/main/java/com/elab/core/log/adapter/Log4jFileAdapter.java
  6. 2 2
      elab-core/src/main/java/com/elab/core/log/adapter/Log4jMongoDBAdapter.java
  7. 2 2
      elab-core/src/main/java/com/elab/core/log/adapter/Slf4jAdapter.java
  8. 6 2
      elab-db/pom.xml
  9. 7 0
      elab-db/src/main/java/com/elab/core/aop/annotations/EnableElabDB.java
  10. 163 29
      elab-db/src/main/java/com/elab/core/dao/BasicBaseDao.java
  11. 2 1
      elab-db/src/main/java/com/elab/core/dao/IBaseDaoSupport.java
  12. 1 1
      elab-db/src/main/java/com/elab/core/dao/row/ThirdRowMapper.java
  13. 20 2
      elab-db/src/main/java/com/elab/core/spring/ClassPathDaoScanner.java
  14. 4 0
      elab-db/src/main/java/com/elab/core/spring/DaoScannerRegister.java
  15. 73 16
      elab-db/src/main/java/com/elab/core/spring/binding/DaoMethod.java
  16. 6 2
      elab-db/src/main/java/com/elab/core/spring/binding/DaoProxy.java
  17. 4 2
      elab-db/src/main/java/com/elab/core/spring/binding/DaoProxyFactory.java
  18. 3 2
      elab-db/src/main/java/com/elab/core/spring/binding/DaoRegister.java
  19. 93 0
      elab-db/src/main/java/com/elab/core/spring/common/SqlInfoModel.java
  20. 9 3
      elab-db/src/main/java/com/elab/core/spring/common/utils/BeanUtils.java
  21. 8 1
      elab-db/src/main/java/com/elab/core/spring/facotry/DaoFactoryBean.java
  22. 42 0
      elab-db/src/main/java/com/elab/core/spring/method/BatchPreparedStatementCreator.java
  23. 22 0
      elab-db/src/main/java/com/elab/core/spring/method/DefaultProxyDataProcess.java
  24. 1 1
      elab-db/src/main/java/com/elab/core/spring/method/DefaultSQLBuilderSupport.java
  25. 30 0
      elab-db/src/main/java/com/elab/core/spring/method/ProxyDataProcess.java
  26. 3 2
      elab-db/src/main/java/com/elab/core/spring/method/SQLEventHandler.java
  27. 4 2
      elab-db/src/main/java/com/elab/core/spring/method/SQLEventHandlerAdaptor.java
  28. 19 0
      elab-db/src/main/java/com/elab/core/sql/config/SQLQueryType.java
  29. 40 2
      elab-db/src/test/java/com.db.service/main/BasicBaseDaoCase.java
  30. 1 0
      elab-db/src/test/java/com.db.service/main/BasicBaseDaoProxyCase.java
  31. 1 2
      elab-db/src/test/java/com.db.service/model/HelloDTO.java
  32. 2 2
      elab-es/pom.xml
  33. 1 1
      elab-es/src/main/resources/META-INF/spring.factories
  34. 4 4
      elab-log/pom.xml
  35. 6 6
      elab-log/src/main/java/com/elab/log/asepct/CatAspect.java
  36. 1 1
      elab-log/src/main/java/com/elab/log/asepct/CatDaoAscept.java
  37. 1 1
      elab-log/src/main/java/com/elab/log/asepct/ExceptionAspect.java
  38. 1 1
      elab-log/src/main/java/com/elab/log/asepct/FeignInterceptor.java
  39. 1 1
      elab-log/src/main/java/com/elab/log/strategy/FeignHystrixConcurrencyStrategy.java
  40. 37 0
      elab-log/src/main/java/com/elab/log/utils/CatCrossProcess.java
  41. 3 0
      elab-log/src/main/java/com/elab/log/utils/CatMsgConstants.java
  42. 1 1
      elab-mongodb/pom.xml
  43. 4 4
      elab-mq/pom.xml
  44. 33 14
      elab-mq/src/main/java/com/elab/mq/listener/AbstractMessageListener.java
  45. 11 0
      elab-mq/src/main/java/com/elab/mq/msg/IMsgProducerFacade.java
  46. 17 2
      elab-mq/src/main/java/com/elab/mq/msg/impl/MsgProducerImpl.java
  47. 44 0
      elab-redis/pom.xml
  48. 62 0
      elab-redis/src/main/java/com/elab/redis/CacheTemplate.java
  49. 44 0
      elab-redis/src/main/java/com/elab/redis/annotation/CacheLoopSubmit.java
  50. 78 0
      elab-redis/src/main/java/com/elab/redis/config/CacheAutoConfiguration.java
  51. 14 0
      elab-redis/src/main/java/com/elab/redis/consts/CacheConstants.java
  52. 24 0
      elab-redis/src/main/java/com/elab/redis/interceptor/BeanFactoryCacheAttributeSourceAdvisor.java
  53. 32 0
      elab-redis/src/main/java/com/elab/redis/interceptor/CacheAttributeSourceAdvisor.java
  54. 31 0
      elab-redis/src/main/java/com/elab/redis/interceptor/CacheAttributeSourcePointcut.java
  55. 36 0
      elab-redis/src/main/java/com/elab/redis/interceptor/CacheInterceptor.java
  56. 42 0
      elab-redis/src/main/java/com/elab/redis/interceptor/ICacheProcessService.java
  57. 114 0
      elab-redis/src/main/java/com/elab/redis/interceptor/impl/CacheLoopProcessImpl.java
  58. 35 0
      elab-redis/src/main/java/com/elab/redis/model/CacheValueModel.java
  59. 30 0
      elab-redis/src/main/java/com/elab/redis/model/annotation/LoopSubmitModel.java
  60. 19 0
      elab-redis/src/main/java/com/elab/redis/redisson/RedissonDecorator.java
  61. 29 0
      elab-redis/src/main/java/com/elab/redis/spring/data/RedisTemplateDecorator.java
  62. 74 0
      elab-redis/src/main/java/com/elab/redis/utils/CacheParseUtil.java
  63. 32 0
      elab-redis/src/test/java/com/elab/redis/RedisSpringBoot.java
  64. 15 0
      elab-redis/src/test/java/com/elab/redis/RedissonApplication.java
  65. 60 0
      elab-redis/src/test/java/com/elab/redis/cache/CacheTest.java
  66. 38 0
      elab-redis/src/test/java/com/elab/redis/redisson/RedisAutoConfigurationTest.java
  67. 45 0
      elab-redis/src/test/java/com/elab/redis/redisson/RedissonAutoConfigurationTest.java
  68. 33 0
      elab-redis/src/test/java/com/elab/redis/redisson/RedissonCacheManagerAutoConfigurationTest.java
  69. 13 0
      elab-redis/src/test/java/com/elab/redis/redisson/RedissonRestApplication.java
  70. 48 0
      elab-redis/src/test/java/com/elab/redis/redisson/RedissonSessionManagerAutoConfigurationTest.java
  71. 25 0
      elab-redis/src/test/java/com/elab/redis/redisson/TestRestController.java
  72. 56 0
      elab-redis/src/test/java/com/elab/redis/redisson/doc/ConfigurationTest.java
  73. 74 0
      elab-redis/src/test/java/com/elab/redis/redisson/doc/DistributedCollectionsTest.java
  74. 20 0
      elab-redis/src/test/java/com/elab/redis/redisson/doc/DistributedLocksAndSynchronizersTest.java
  75. 145 0
      elab-redis/src/test/java/com/elab/redis/redisson/doc/DistributedTest.java
  76. 31 0
      elab-redis/src/test/java/com/elab/redis/redisson/doc/model/AnyObject.java
  77. 33 0
      elab-redis/src/test/java/com/elab/redis/redisson/doc/model/BloomFilterObject.java
  78. 33 0
      elab-redis/src/test/java/com/elab/redis/redisson/doc/model/SomeObject.java
  79. 20 0
      elab-redis/src/test/java/com/elab/redis/service/IDemoService.java
  80. 51 0
      elab-redis/src/test/java/com/elab/redis/service/impl/DemoServiceImpl.java
  81. 25 0
      elab-redis/src/test/java/com/elab/redis/spring/SpringDataTest.java
  82. 48 0
      elab-redis/src/test/java/com/elab/redis/utils/TestUtils.java
  83. 10 0
      elab-redis/src/test/resources/application.yml
  84. 2 2
      elab-rocketMQ/pom.xml
  85. 162 0
      elab-spring/README.md
  86. 13 1
      elab-spring/pom.xml
  87. 49 0
      elab-spring/src/main/java/com/elab/spring/callback/IRestFallback.java
  88. 329 0
      elab-spring/src/main/java/com/elab/spring/callback/impl/DefaultRestFallBack.java
  89. 32 0
      elab-spring/src/main/java/com/elab/spring/callback/impl/RestFallbackAdaptor.java
  90. 19 0
      elab-spring/src/main/java/com/elab/spring/dao/MngHttpFailureDataDao.java
  91. 17 0
      elab-spring/src/main/java/com/elab/spring/dao/MngHttpInfoDao.java
  92. 337 0
      elab-spring/src/main/java/com/elab/spring/dao/entity/MngHttpFailureDataEntity.java
  93. 308 0
      elab-spring/src/main/java/com/elab/spring/dao/entity/MngHttpInfoEntity.java
  94. 2 2
      elab-spring/src/main/java/com/elab/spring/exception/CommonException.java
  95. 14 0
      elab-spring/src/main/java/com/elab/spring/exception/rest/CoolingException.java
  96. 138 0
      elab-spring/src/main/java/com/elab/spring/factory/HttpsClientRequestFactory.java
  97. 40 0
      elab-spring/src/main/java/com/elab/spring/model/rest/HttpUrlMetric.java
  98. 82 11
      elab-spring/src/main/java/com/elab/spring/utils/RestTemplateUtils.java
  99. 63 1
      elab-spring/src/test/java/com/elab/spring/utils/RestTemplateUtilsTest.java
  100. 0 0
      elab-spring/src/test/resources/application-dev.yml

+ 1 - 1
elab-alert/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>elab-parent</artifactId>
         <groupId>com.elab.core</groupId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>elab-alert</artifactId>

+ 1 - 1
elab-annotation/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>elab-parent</artifactId>
         <groupId>com.elab.core</groupId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 3 - 3
elab-cache/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.elab.core</groupId>
         <artifactId>elab-parent</artifactId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
     <groupId>com.elab.cache</groupId>
     <artifactId>elab-cache</artifactId>
@@ -132,7 +132,7 @@
                 <!--<plugin>-->
                     <!--<artifactId>maven-clean-plugin</artifactId>-->
                     <!--<version>${maven.clean.plugin}</version>-->
-                    <!--<configuration>-->
+                    <!--<doc>-->
                         <!--<filesets>-->
                             <!--<fileset>-->
                                 <!--<directory>${basedir}/src/main/webapp/WEB-INF/classes</directory>-->
@@ -141,7 +141,7 @@
                                 <!--<directory>${basedir}/src/main/webapp/WEB-INF/lib</directory>-->
                             <!--</fileset>-->
                         <!--</filesets>-->
-                    <!--</configuration>-->
+                    <!--</doc>-->
                 <!--</plugin>-->
 
                 <plugin>

+ 3 - 3
elab-core/pom.xml

@@ -7,7 +7,7 @@
     <parent>
         <groupId>com.elab.core</groupId>
         <artifactId>elab-parent</artifactId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
 
     <groupId>com.elab.core</groupId>
@@ -56,12 +56,12 @@
                         <!--<goals>-->
                             <!--<goal>jar</goal>-->
                         <!--</goals>-->
-                        <!--<configuration>-->
+                        <!--<doc>-->
                             <!--<additionalparam>-Xdoclint:none</additionalparam>-->
                             <!--<charset>UTF-8</charset>-->
                             <!--<encoding>UTF-8</encoding>-->
                             <!--<docencoding>UTF-8</docencoding>-->
-                        <!--</configuration>-->
+                        <!--</doc>-->
                     <!--</execution>-->
                 <!--</executions>-->
             <!--</plugin>-->

+ 2 - 2
elab-core/src/main/java/com/elab/core/log/adapter/Log4jFileAdapter.java

@@ -1,7 +1,7 @@
 //package com.elab.core.log.adapter;
 //
-//import com.elab.core.configuration.ConfigManager;
-//import com.elab.core.configuration.entity.LogConfig;
+//import com.elab.core.doc.ConfigManager;
+//import com.elab.core.doc.entity.LogConfig;
 //import com.elab.core.log.ILogAdapter;
 //import com.elab.core.log.LogEntry;
 //import com.elab.core.log.LogLevel;

+ 2 - 2
elab-core/src/main/java/com/elab/core/log/adapter/Log4jMongoDBAdapter.java

@@ -1,7 +1,7 @@
 //package com.elab.core.log.adapter;
 //
-//import com.elab.core.configuration.ConfigManager;
-//import com.elab.core.configuration.entity.LogConfig;
+//import com.elab.core.doc.ConfigManager;
+//import com.elab.core.doc.entity.LogConfig;
 //import com.elab.core.log.ILogAdapter;
 //import com.elab.core.log.LogEntry;
 //import com.elab.core.log.LogLevel;

+ 2 - 2
elab-core/src/main/java/com/elab/core/log/adapter/Slf4jAdapter.java

@@ -1,7 +1,7 @@
 //package com.elab.core.log.adapter;
 //
-//import com.elab.core.configuration.ConfigManager;
-//import com.elab.core.configuration.entity.LogConfig;
+//import com.elab.core.doc.ConfigManager;
+//import com.elab.core.doc.entity.LogConfig;
 //import com.elab.core.log.ILogAdapter;
 //import com.elab.core.log.LogEntry;
 //import com.elab.core.log.LogLevel;

+ 6 - 2
elab-db/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.elab.core</groupId>
         <artifactId>elab-parent</artifactId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>elab-db</artifactId>
@@ -24,7 +24,11 @@
 
     <dependencies>
         <!-- https://mvnrepository.com/artifact/ma.glasnost.orika/orika-core -->
-
+        <!--<dependency>-->
+            <!--<groupId>org.springframework</groupId>-->
+            <!--<artifactId>spring-jdbc</artifactId>-->
+            <!--<version>5.1.3.RELEASE</version>-->
+        <!--</dependency>-->
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>

+ 7 - 0
elab-db/src/main/java/com/elab/core/aop/annotations/EnableElabDB.java

@@ -52,4 +52,11 @@ public @interface EnableElabDB {
      */
     String sqlConfigurableLocations() default "sql";
 
+    /**
+     * 数据代理,一旦启用数据路由的SQL将会转发到指定的实现类上来
+     *
+     * @return
+     */
+    String proxyDataProcessName() default "";
+
 }

+ 163 - 29
elab-db/src/main/java/com/elab/core/dao/BasicBaseDao.java

@@ -1,5 +1,6 @@
 package com.elab.core.dao;
 
+import com.alibaba.fastjson.JSON;
 import com.elab.core.bean.PageModel;
 import com.elab.core.dao.model.JdbcParamsModel;
 import com.elab.core.dao.model.ListDynamicSearch;
@@ -9,6 +10,7 @@ import com.elab.core.dao.params.NamedParameterUtils2;
 import com.elab.core.dao.params.ParsedSql2;
 import com.elab.core.dao.row.ThirdRowMapper;
 import com.elab.core.exception.CoreException;
+import com.elab.core.spring.method.BatchPreparedStatementCreator;
 import com.elab.core.spring.method.CheckSqlProcess;
 import com.elab.core.spring.method.DefaultCheckSQLProcess;
 import com.elab.core.spring.method.SQLEventHandler;
@@ -23,15 +25,15 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.EmptyResultDataAccessException;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.RowMapper;
-import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
-import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
-import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
-import org.springframework.jdbc.core.namedparam.SqlParameterSource;
+import org.springframework.jdbc.core.*;
+import org.springframework.jdbc.core.namedparam.*;
 import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.JdbcUtils;
 
 import javax.persistence.Column;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
 import java.util.*;
 
 /**
@@ -241,10 +243,12 @@ public class BasicBaseDao implements ApplicationContextAware,
     }
 
     /**
-     * @param sql
-     * @param obj
-     * @param search
-     * @param elementType
+     * 查询一个集合对象并返回指定的类型
+     *
+     * @param sql         SQL语句
+     * @param obj         SQL执行参数
+     * @param search      查询补充的参数
+     * @param elementType 返回的数据类型
      * @param <T>
      * @return
      * @throws Exception
@@ -252,6 +256,11 @@ public class BasicBaseDao implements ApplicationContextAware,
     public <T> List<T> executeQueryList(String sql, Object obj, ListDynamicSearch search, Class<T> elementType) throws
             Exception {
         JdbcParamsModel jdbcParamsModel = commonParseSql(sql, obj, search);
+        List<T> ts = findForList(elementType, jdbcParamsModel);
+        return ts;
+    }
+
+    private <T> List<T> findForList(Class<T> elementType, JdbcParamsModel jdbcParamsModel) {
         long start = System.currentTimeMillis();
         List<T> ts = (List<T>) this.jdbcTemplate.query(jdbcParamsModel.getSql(), jdbcParamsModel.getObjects(), new TimeRowMapperResultSetExtractor(new ThirdRowMapper<T>(elementType)));
         long time = System.currentTimeMillis() - start;
@@ -316,8 +325,8 @@ public class BasicBaseDao implements ApplicationContextAware,
      * @param o   参数
      * @return
      */
-    public List<Map<String, Object>> executeQueryForListMap(String sql, Object o) throws Exception {
-        JdbcParamsModel jdbcParamsModel = commonParseSql(sql, o);
+    public List<Map<String, Object>> executeQueryForListMap(String sql, Object o, ListDynamicSearch dynamicSearch) throws Exception {
+        JdbcParamsModel jdbcParamsModel = commonParseSql(sql, o, dynamicSearch);
         long start = System.currentTimeMillis();
         List<Map<String, Object>> maps = this.jdbcTemplate.queryForList(jdbcParamsModel.getSql(), jdbcParamsModel.getObjects());
         long time = System.currentTimeMillis() - start;
@@ -461,6 +470,9 @@ public class BasicBaseDao implements ApplicationContextAware,
         return map;
     }
 
+    public int insert(String sql, Object o) throws Exception {
+        return insert("", "", sql, o);
+    }
 
     /**
      * 添加数据操作
@@ -469,18 +481,22 @@ public class BasicBaseDao implements ApplicationContextAware,
      * @param o   参数对象
      * @return
      */
-    public int insert(String sql, Object o) {
+    public int insert(String sqlId, String table, String sql, Object o) throws Exception {
+        int result = 0;
         SqlParameterSource sqlParameterSource = getSqlParameterSource(o);
         GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
-        sqlInvokeBefore(SqlCommandType.INSERT, sql, o);
+        sqlInvokeBefore(SqlCommandType.INSERT, sqlId, table, sql, o);
         long start = System.currentTimeMillis();
-        logger.debug("sql : " + sql);
+        logger.debug("sql : " + sql + "\r\n" + JSON.toJSONString(o));
         this.getNamedParameterJdbcTemplate().update(sql, sqlParameterSource, keyHolder);
-        int result = keyHolder.getKey().intValue();
-        logger.debug(" params : " + result);
+        // 如果存在自动生成的主键,不存在那么表示参数中已经涵盖了,不需要返回。
+        if (keyHolder.getKey() != null) {
+            result = keyHolder.getKey().intValue();
+        }
+        logger.debug(" 得到主键 : " + result);
         long time = System.currentTimeMillis() - start;
         logger.debug(" SQL 执行耗时 : " + time);
-        sqlInvokeAfter(SqlCommandType.INSERT, sql, o, result);
+        sqlInvokeAfter(SqlCommandType.INSERT, sqlId, table, sql, o, result);
         return result;
     }
 
@@ -503,14 +519,21 @@ public class BasicBaseDao implements ApplicationContextAware,
         return sqlParameterSource;
     }
 
+    public int[] batchOperation(SqlCommandType sqlCommandType, String sql, List list)
+            throws Exception {
+        return batchOperation(sqlCommandType, "", "", sql, list);
+    }
+
     /**
      * 批量操作
      *
-     * @param sql  sql语句
-     * @param list 数据集合
+     * @param sqlCommandType 执行的SQL命令类型
+     * @param sql            sql语句
+     * @param list           数据集合
      * @return
      */
-    public int[] batchOperation(String sql, List list) throws Exception {
+    public int[] batchOperation(SqlCommandType sqlCommandType, String sqlId, String table, String sql, List list) throws
+            Exception {
         if (list != null && list.size() > 0) {
             long start = System.currentTimeMillis();
             logger.debug("sql : " + sql);
@@ -529,7 +552,16 @@ public class BasicBaseDao implements ApplicationContextAware,
                 SqlParameterSource sqlParameterSource = getConvertSqlParameterSource(bean, nameMap);
                 parameterSource[i] = sqlParameterSource;
             }
-            int[] results = this.getNamedParameterJdbcTemplate().batchUpdate(sql, parameterSource);
+
+            int[] results;
+            sqlInvokeBefore(sqlCommandType, sqlId, table, sql, list);
+            // 如果是批量新增的情况,则要将新增的批量编号进行返回
+            if (SqlCommandType.INSERT == sqlCommandType) {
+                results = batchInsert(sql, parameterSource);
+            } else {
+                results = this.getNamedParameterJdbcTemplate().batchUpdate(sql, parameterSource);
+            }
+            sqlInvokeAfter(sqlCommandType, sqlId, table, sql, list, results);
             logger.debug(" params size : " + list.size());
             long time = System.currentTimeMillis() - start;
             logger.debug(" SQL 执行耗时 : " + time);
@@ -539,24 +571,126 @@ public class BasicBaseDao implements ApplicationContextAware,
         }
     }
 
-    private void sqlInvokeBefore(SqlCommandType event, String sql, Object o) {
+    /**
+     * 批量返回新增结果
+     *
+     * @param sql             要执行的SQL
+     * @param parameterSource 参数对象
+     * @return
+     * @throws SQLException
+     */
+    private int[] batchInsert(String sql, SqlParameterSource[] parameterSource) throws SQLException {
+        ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
+        String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, parameterSource[0]);
+        List<SqlParameter> declaredParameters = NamedParameterUtils.buildSqlParameterList(parsedSql, parameterSource[0]);
+        PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sqlToUse, declaredParameters);
+        /**
+         * 参数执行处理
+         */
+        BatchPreparedStatementSetter pss = new BatchPreparedStatementSetter() {
+            @Override
+            public void setValues(PreparedStatement ps, int i) throws SQLException {
+                // 将SQL的中的:参数转化成数组
+                Object[] values = NamedParameterUtils.buildValueArray(parsedSql, parameterSource[i], null);
+                // 转化成数组之后一个个和?对应填充
+                pscf.newPreparedStatementSetter(values).setValues(ps);
+            }
+
+            @Override
+            public int getBatchSize() {
+                return parameterSource.length;
+            }
+        };
+        /**
+         * 创建Statement对象的时候,加上Statement.RETURN_GENERATED_KEYS参数
+         */
+        BatchPreparedStatementCreator batchPreparedStatementCreator = new BatchPreparedStatementCreator(sqlToUse);
+        return jdbcTemplate.execute(batchPreparedStatementCreator, (PreparedStatementCallback<int[]>) ps -> {
+            try {
+                int batchSize = pss.getBatchSize();
+                int[] keys = new int[batchSize];
+
+                InterruptibleBatchPreparedStatementSetter ipss =
+                        (pss instanceof InterruptibleBatchPreparedStatementSetter ?
+                                (InterruptibleBatchPreparedStatementSetter) pss : null);
+                if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
+                    for (int i = 0; i < batchSize; i++) {
+                        pss.setValues(ps, i);
+                        if (ipss != null && ipss.isBatchExhausted(i)) {
+                            break;
+                        }
+                        ps.addBatch();
+                    }
+                    ps.executeBatch();
+                    // 将插入的主键值返回出来
+                    ResultSet rs = ps.getGeneratedKeys();
+                    int index = 0;
+                    while (rs.next() && index < batchSize) {
+                        keys[index] = rs.getInt(1);
+                        index++;
+                    }
+                    return keys;
+                } else {
+                    List<Integer> rowsAffected = new ArrayList<>();
+                    for (int i = 0; i < batchSize; i++) {
+                        pss.setValues(ps, i);
+                        if (ipss != null && ipss.isBatchExhausted(i)) {
+                            break;
+                        }
+                        rowsAffected.add(ps.executeUpdate());
+                    }
+                    int[] rowsAffectedArray = new int[rowsAffected.size()];
+                    for (int i = 0; i < rowsAffectedArray.length; i++) {
+                        rowsAffectedArray[i] = rowsAffected.get(i);
+                    }
+                    return rowsAffectedArray;
+                }
+            } finally {
+                if (pss instanceof ParameterDisposer) {
+                    ((ParameterDisposer) pss).cleanupParameters();
+                }
+            }
+        });
+    }
+
+    protected PreparedStatementCreatorFactory getPreparedStatementCreatorFactory(
+            ParsedSql parsedSql, SqlParameterSource paramSource) {
+        String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
+        List<SqlParameter> declaredParameters = NamedParameterUtils.buildSqlParameterList(parsedSql, paramSource);
+        return new PreparedStatementCreatorFactory(sqlToUse, declaredParameters);
+    }
+
+    private void sqlInvokeBefore(SqlCommandType event, String sqlId, String table, String sql, Object o) {
         if (this.sqlEventHandlerList != null && this.sqlEventHandlerList.size() > 0) {
             for (int i = 0; i < sqlEventHandlerList.size(); i++) {
                 SQLEventHandler sqlEventHandler = sqlEventHandlerList.get(i);
-                sqlEventHandler.sqlInvokeBefore(event, sql, o);
+                sqlEventHandler.sqlInvokeBefore(event, sqlId, table, sql, o);
             }
         }
     }
 
-    private void sqlInvokeAfter(SqlCommandType event, String sql, Object request, Object result) {
+    private void sqlInvokeAfter(SqlCommandType event, String sqlId, String table, String sql, Object request, Object
+            result) {
         if (this.sqlEventHandlerList != null && this.sqlEventHandlerList.size() > 0) {
             for (int i = 0; i < sqlEventHandlerList.size(); i++) {
                 SQLEventHandler sqlEventHandler = sqlEventHandlerList.get(i);
-                sqlEventHandler.sqlInvokeAfter(event, sql, request, result);
+                sqlEventHandler.sqlInvokeAfter(event, sqlId, table, sql, request, result);
             }
         }
     }
 
+    /**
+     * 执行修改
+     *
+     * @param sql
+     * @param o
+     * @return
+     * @throws Exception
+     */
+    public int update(String sql, Object o) throws Exception {
+        return update("", "", sql, o);
+    }
+
     /**
      * 修改数据操作
      *
@@ -564,13 +698,13 @@ public class BasicBaseDao implements ApplicationContextAware,
      * @param o   对象
      * @return
      */
-    public int update(String sql, Object o) throws Exception {
+    public int update(String sqlId, String table, String sql, Object o) throws Exception {
         JdbcParamsModel jdbcParamsModel = commonParseSql(sql, o);
-        sqlInvokeBefore(SqlCommandType.UPDATE, sql, o);
+        sqlInvokeBefore(SqlCommandType.UPDATE, sqlId, table, sql, o);
         long start = System.currentTimeMillis();
         int update = this.jdbcTemplate.update(jdbcParamsModel.getSql(), jdbcParamsModel.getObjects());
         long time = System.currentTimeMillis() - start;
-        sqlInvokeAfter(SqlCommandType.UPDATE, sql, o, update);
+        sqlInvokeAfter(SqlCommandType.UPDATE, sqlId, table, sql, o, update);
         logger.debug(" SQL 执行耗时 : " + time);
         return update;
     }

+ 2 - 1
elab-db/src/main/java/com/elab/core/dao/IBaseDaoSupport.java

@@ -2,6 +2,7 @@ package com.elab.core.dao;/**
  * Created by liukx on 2018/1/4.
  */
 
+import com.elab.core.aop.annotations.FieldParam;
 import com.elab.core.bean.PageModel;
 import com.elab.core.dao.model.ListDynamicSearch;
 import org.springframework.jdbc.core.RowMapper;
@@ -61,7 +62,7 @@ public interface IBaseDaoSupport<T> {
      * @return
      * @throws Exception
      */
-    public T selectById(String id) throws Exception;
+    public T selectById(@FieldParam("id") String id) throws Exception;
 
     /**
      * 查询一个集合,参数可以是任意的

+ 1 - 1
elab-db/src/main/java/com/elab/core/dao/row/ThirdRowMapper.java

@@ -63,7 +63,7 @@ public class ThirdRowMapper<T> implements RowMapper<T> {
 
 
     /**
-     * Create a new BeanPropertyRowMapper for bean-style configuration.
+     * Create a new BeanPropertyRowMapper for bean-style doc.
      *
      * @see #setMappedClass
      * @see #setCheckFullyPopulated

+ 20 - 2
elab-db/src/main/java/com/elab/core/spring/ClassPathDaoScanner.java

@@ -6,6 +6,8 @@ import com.elab.core.dao.IExampleDaoSupport;
 import com.elab.core.spring.binding.DaoRegister;
 import com.elab.core.spring.common.utils.StringUtils;
 import com.elab.core.spring.facotry.DaoFactoryBean;
+import com.elab.core.spring.method.DefaultProxyDataProcess;
+import com.elab.core.spring.method.ProxyDataProcess;
 import com.elab.core.sql.ConfigurableFactory;
 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
 import org.springframework.beans.factory.config.BeanDefinitionHolder;
@@ -38,8 +40,14 @@ public class ClassPathDaoScanner extends ClassPathBeanDefinitionScanner {
     // 具体的接口
     private Class<?> markerInterface;
     private DaoRegister daoRegister;
-    private ConfigurableFactory configurableFactory;
 
+    private String proxyDataProcessName;
+
+    private ConfigurableFactory configurableFactory;
+    /**
+     * 代理数据处理器
+     */
+    private ProxyDataProcess proxyDataProcess = new DefaultProxyDataProcess();
 
     public DaoRegister getDaoRegister() {
         if (this.daoRegister == null) {
@@ -48,6 +56,10 @@ public class ClassPathDaoScanner extends ClassPathBeanDefinitionScanner {
         return daoRegister;
     }
 
+    public void setProxyDataProcessName(String proxyDataProcessName) {
+        this.proxyDataProcessName = proxyDataProcessName;
+    }
+
     public ConfigurableFactory getConfigurableFactory() {
         return configurableFactory;
     }
@@ -101,7 +113,7 @@ public class ClassPathDaoScanner extends ClassPathBeanDefinitionScanner {
     public Set<BeanDefinitionHolder> doScan(String... basePackages) {
         Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
         if (beanDefinitions.isEmpty()) {
-            logger.warn("No jdbc dao was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
+            logger.warn("No jdbc dao was found in '" + Arrays.toString(basePackages) + "' package. Please check your doc.");
         } else {
             for (BeanDefinitionHolder holder : beanDefinitions) {
                 GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
@@ -131,6 +143,12 @@ public class ClassPathDaoScanner extends ClassPathBeanDefinitionScanner {
                 } else {
                     logger.error(" basicBaseDaoName is not null !!!");
                 }
+                if (!StringUtils.isEmpty(this.proxyDataProcessName)) {
+                    definition.getPropertyValues().add("proxyDataProcess", new RuntimeBeanReference
+                            (proxyDataProcessName));
+                } else {
+                    definition.getPropertyValues().add("proxyDataProcess", proxyDataProcess);
+                }
                 definition.getPropertyValues().add("daoRegister", getDaoRegister());
                 definition.getPropertyValues().add("configurableFactory", configurableFactory);
                 explicitFactoryUsed = true;

+ 4 - 0
elab-db/src/main/java/com/elab/core/spring/DaoScannerRegister.java

@@ -55,6 +55,10 @@ public class DaoScannerRegister implements ImportBeanDefinitionRegistrar, Applic
                         ());
                 ClassPathDaoScanner scanner = new ClassPathDaoScanner(beanDefinitionRegistry);
                 scanner.setBasicBaseDaoName("basicBaseDao");
+                String proxyDataProcessName = defaultAttrs.get("proxyDataProcessName").toString();
+                if (!StringUtils.isEmpty(proxyDataProcessName)) {
+                    scanner.setProxyDataProcessName(proxyDataProcessName);
+                }
                 scanner.setResourceLoader(this.applicationContext);
                 scanner.setConfigurableFactory(sqlConfigurableLocations);
                 //

+ 73 - 16
elab-db/src/main/java/com/elab/core/spring/binding/DaoMethod.java

@@ -8,11 +8,13 @@ import com.elab.core.dao.IExampleDaoSupport;
 import com.elab.core.dao.model.ListDynamicSearch;
 import com.elab.core.dao.row.JoinRowMapper;
 import com.elab.core.spring.base.EntityOperation;
+import com.elab.core.spring.common.SqlInfoModel;
 import com.elab.core.spring.common.TypeGroupModel;
 import com.elab.core.spring.common.utils.BeanUtils;
 import com.elab.core.spring.common.utils.DataUtils;
 import com.elab.core.spring.method.DefaultSQLBuilderSupport;
 import com.elab.core.spring.method.ISQLBuliderSupport;
+import com.elab.core.spring.method.ProxyDataProcess;
 import com.elab.core.sql.ConfigurableFactory;
 import com.elab.core.sql.config.SqlCommandType;
 import com.elab.core.utils.StringUtils;
@@ -102,18 +104,21 @@ public class DaoMethod {
      */
     private String xmlGroupName;
 
+    private ProxyDataProcess proxyDataProcess;
+
     private Logger logger = LoggerFactory.getLogger(DaoMethod.class);
 
     /**
      * 构建一个DaoMethod的方法对象
      *
+     * @param <T>
      * @param mapperInterface     dao的接口類
      * @param method              方法信息
      * @param basicBaseDao        jdbc操作类
      * @param configurableFactory 配置工厂
-     * @param <T>
+     * @param proxyDataProcess
      */
-    public <T> DaoMethod(Class<?> mapperInterface, Method method, BasicBaseDao basicBaseDao, ConfigurableFactory configurableFactory) {
+    public <T> DaoMethod(Class<?> mapperInterface, Method method, BasicBaseDao basicBaseDao, ConfigurableFactory configurableFactory, ProxyDataProcess proxyDataProcess) {
         // 接口信息
         this.mapperInterface = mapperInterface;
         // 方法信息
@@ -133,6 +138,8 @@ public class DaoMethod {
         this.supperGenricType = BeanUtils.getClassByString(classGenricTypeString.get(0));
         // 第二个,案例对象
         this.exampleType = classGenricTypeString.size() > 1 ? BeanUtils.getClassByString(classGenricTypeString.get(1)) : null;
+        // 代理数据执行器
+        this.proxyDataProcess = proxyDataProcess;
         // 实体操作类
         this.entityOperation = new EntityOperation(this.supperGenricType, "id");
         if (this.mapperInterface.isAnnotationPresent(XmlGroupName.class)) {
@@ -153,12 +160,14 @@ public class DaoMethod {
         int argsLength = args.length;
         String sqlSource = method.getName();
         String statementName = getSqlId();
+        String tableName = this.entityOperation.tableName;
 //        // TODO 待优化 , 可以获取该方法的全称,然后去解析里面的参数名称
 //        if (argsLength != 1) {
 //            throw new IllegalArgumentException(sqlSource + " 方法 参数尽量保持在1个,可以是Map,可以是Object");
 //        }
         Object arg = args[0];
         SqlCommandType sqlType = this.configurableFactory.getSqlConfigurableFactory().getSqlType(statementName);
+
         if (sqlType == null) {
             // 表示配置文件中没有找到,则会从父类接口去获取
             if (!StringUtils.isEmpty(this.supperInterfaceName)) {
@@ -181,23 +190,24 @@ public class DaoMethod {
             throw new Exception(" 找不到[" + statementName + "]对应的处理sql");
         } else {
             String sql = this.configurableFactory.getSqlConfigurableFactory().getSql(statementName);
+            logger.debug(" SQL ID : " + statementName);
             if (sqlType == SqlCommandType.INSERT) {
                 if (arg instanceof List) {
-                    return this.basicBaseDao.batchOperation(sql, (List) arg);
+                    return this.basicBaseDao.batchOperation(sqlType, statementName, tableName, sql, (List) arg);
                 } else {
-                    return this.basicBaseDao.insert(sql, arg);
+                    return this.basicBaseDao.insert(statementName, tableName, sql, arg);
                 }
             } else if (sqlType == SqlCommandType.UPDATE || sqlType == SqlCommandType.DELETE) {
                 if (arg instanceof List) {
-                    return this.basicBaseDao.batchOperation(sql, (List) arg);
+                    return this.basicBaseDao.batchOperation(sqlType, statementName, tableName, sql, (List) arg);
                 }
-                return this.basicBaseDao.update(sql, arg);
+                return this.basicBaseDao.update(statementName, tableName, sql, arg);
             } else {
                 if (sqlType != SqlCommandType.SELECT) {
                     System.out.println("语句类型有误!");
                     return null;
                 }
-                return selectInvokeMehtod(sql, args);
+                return selectInvokeMethod(statementName, sql, args);
             }
         }
 
@@ -305,15 +315,13 @@ public class DaoMethod {
     /**
      * 查询执行方法
      *
-     * @param sql
-     * @param args 参数列表
+     * @param statementName SQL的唯一编号
+     * @param sql           SQL执行的内容
+     * @param args          参数列表
      * @return
      */
-    private Object selectInvokeMehtod(String sql, Object[] args) throws Exception {
+    private Object selectInvokeMethod(String statementName, String sql, Object[] args) throws Exception {
         Class<?> returnType = this.method.getReturnType();
-        boolean returnsMany = (returnType == List.class);
-        boolean returnsMap = (returnType == Map.class);
-        boolean returnPage = (returnType == PageModel.class);
         TypeGroupModel paramsByType = DataUtils.getParamsByType(this.method, args);
         Object arg = args[0];
         // 确定最终要执行的入参对象
@@ -323,14 +331,39 @@ public class DaoMethod {
         } else if (paramsByType.getObj() != null && paramsByType.getObj().length > 0) {
             arg = paramsByType.getObj()[0];
         }
+        // 构建SQL相关的对象信息
+        SqlInfoModel sqlInfo = getSqlInfo(statementName, sql, returnType, arg, paramsByType);
+        // 这里决定是否代理
+        Object proxy = proxyDataProcess.isProxy(sqlInfo);
+        if (proxy != null) {
+            logger.debug("此次结构被代理 , 触发代理的规则 : " + proxy);
+            return proxyDataProcess.proxyQueryData(proxy, sqlInfo);
+        } else {
+            return processSelectSQL(sql, returnType, paramsByType, arg);
+        }
+    }
+
+    /**
+     * 执行查询SQL
+     *
+     * @param sql          SQL内容
+     * @param returnType   SQL的返回结果
+     * @param paramsByType SQL的特殊参数对象
+     * @param arg          SQL的正常参数对象
+     * @return
+     * @throws Exception
+     */
+    private Object processSelectSQL(String sql, Class<?> returnType, TypeGroupModel paramsByType, Object arg) throws Exception {
+        boolean returnsMany = (returnType == List.class);
+        boolean returnsMap = (returnType == Map.class);
+        boolean returnPage = (returnType == PageModel.class);
         // 如果需要追加动态参数对象处理
         ListDynamicSearch listDynamicSearch = paramsByType.getListDynamicSearch();
-
         // 是否是返回多个对象,例如List
         if (returnsMany) {
             Class typeClass = getCacheMethodReturnClass();
             if (typeClass == Map.class) {
-                return this.basicBaseDao.executeQueryForListMap(sql, arg);
+                return this.basicBaseDao.executeQueryForListMap(sql, arg, listDynamicSearch);
             } else {
                 RowMapper rowMapper = paramsByType.getRowMapper();
                 if (rowMapper == null) {
@@ -360,7 +393,7 @@ public class DaoMethod {
             String sqlLimit = " " + sql + " " + pageModel.getOrderby() + "  limit " + (pageModel.getCount() - 1) * pageModel.getPageSize() + "," + pageModel.getPageSize();
             List list = null;
             if (typeClass == Map.class) {
-                list = this.basicBaseDao.executeQueryForListMap(sqlLimit, arg);
+                list = this.basicBaseDao.executeQueryForListMap(sqlLimit, arg, listDynamicSearch);
             } else {
                 list = this.basicBaseDao.executeQueryList(sqlLimit, arg, typeClass);
             }
@@ -379,6 +412,30 @@ public class DaoMethod {
         }
     }
 
+    /**
+     * 构建SQL相关的信息对象
+     *
+     * @param statementName SQL的id
+     * @param sql           SQL的原始内容
+     * @param returnType    SQL的返回类型
+     * @param arg           SQL的参数
+     * @param paramsByType  SQL的特殊参数组对象
+     * @return
+     */
+    private SqlInfoModel getSqlInfo(String statementName, String sql, Class<?> returnType, Object arg,
+                                    TypeGroupModel paramsByType) {
+        SqlInfoModel sqlInfoModel = new SqlInfoModel();
+        sqlInfoModel.setTableName(this.entityOperation.tableName);
+        sqlInfoModel.setArgs(arg);
+        sqlInfoModel.setSql(sql);
+        sqlInfoModel.setReturnType(returnType);
+        sqlInfoModel.setSqlId(statementName);
+        sqlInfoModel.setReturnClass(getCacheMethodReturnClass());
+        sqlInfoModel.setParamGroup(paramsByType);
+        sqlInfoModel.setMapType(this.method.getGenericReturnType());
+        return sqlInfoModel;
+    }
+
 
     /**
      * 获取方法返回值

+ 6 - 2
elab-db/src/main/java/com/elab/core/spring/binding/DaoProxy.java

@@ -1,6 +1,7 @@
 package com.elab.core.spring.binding;
 
 import com.elab.core.dao.BasicBaseDao;
+import com.elab.core.spring.method.ProxyDataProcess;
 import com.elab.core.sql.ConfigurableFactory;
 
 import java.io.Serializable;
@@ -24,12 +25,14 @@ public class DaoProxy<T> implements InvocationHandler, Serializable {
     private final Class<T> mapperInterface;
     private final Map<Method, DaoMethod> methodCache;
     private ConfigurableFactory configurableFactory;
+    private ProxyDataProcess proxyDataProcess;
 
-    public DaoProxy(BasicBaseDao basicBaseDao, Class<T> mapperInterface, Map<Method, DaoMethod> methodCache, ConfigurableFactory configurableFactory) {
+    public DaoProxy(BasicBaseDao basicBaseDao, Class<T> mapperInterface, Map<Method, DaoMethod> methodCache, ConfigurableFactory configurableFactory, ProxyDataProcess proxyDataProcess) {
         this.basicBaseDao = basicBaseDao;
         this.mapperInterface = mapperInterface;
         this.methodCache = methodCache;
         this.configurableFactory = configurableFactory;
+        this.proxyDataProcess = proxyDataProcess;
     }
 
     @Override
@@ -55,7 +58,8 @@ public class DaoProxy<T> implements InvocationHandler, Serializable {
     private DaoMethod cachedMapperMethod(Method method) {
         DaoMethod daoMethod = this.methodCache.get(method);
         if (daoMethod == null) {
-            daoMethod = new DaoMethod(this.mapperInterface, method, basicBaseDao, configurableFactory);
+            daoMethod = new DaoMethod(this.mapperInterface, method, this.basicBaseDao, this.configurableFactory, this
+                    .proxyDataProcess);
             this.methodCache.put(method, daoMethod);
         }
         return daoMethod;

+ 4 - 2
elab-db/src/main/java/com/elab/core/spring/binding/DaoProxyFactory.java

@@ -1,6 +1,7 @@
 package com.elab.core.spring.binding;
 
 import com.elab.core.dao.BasicBaseDao;
+import com.elab.core.spring.method.ProxyDataProcess;
 import com.elab.core.sql.ConfigurableFactory;
 
 import java.lang.reflect.Method;
@@ -39,8 +40,9 @@ public class DaoProxyFactory<T> {
         return (T) Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
     }
 
-    public T newInstance(BasicBaseDao basicBaseDao, ConfigurableFactory configurableFactory) {
-        DaoProxy<T> mapperProxy = new DaoProxy(basicBaseDao, this.mapperInterface, this.methodCache, configurableFactory);
+    public T newInstance(BasicBaseDao basicBaseDao, ConfigurableFactory configurableFactory, ProxyDataProcess proxyDataProcess) {
+        DaoProxy<T> mapperProxy = new DaoProxy(basicBaseDao, this.mapperInterface, this.methodCache,
+                configurableFactory,proxyDataProcess);
         return this.newInstance(mapperProxy);
     }
 }

+ 3 - 2
elab-db/src/main/java/com/elab/core/spring/binding/DaoRegister.java

@@ -1,6 +1,7 @@
 package com.elab.core.spring.binding;
 
 import com.elab.core.dao.BasicBaseDao;
+import com.elab.core.spring.method.ProxyDataProcess;
 import com.elab.core.sql.ConfigurableFactory;
 
 import java.util.HashMap;
@@ -21,13 +22,13 @@ public class DaoRegister<T> {
     // 承装动态代理的容器
     private final Map<Class<?>, DaoProxyFactory> knownMappers = new HashMap();
 
-    public <T> T getDao(Class<T> type, BasicBaseDao basicBaseDao, ConfigurableFactory configurableFactory) {
+    public <T> T getDao(Class<T> type, BasicBaseDao basicBaseDao, ConfigurableFactory configurableFactory, ProxyDataProcess proxyDataProcess) {
         DaoProxyFactory daoProxyFactory = this.knownMappers.get(type);
         if (daoProxyFactory == null) {
             throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
         } else {
             try {
-                return (T) daoProxyFactory.newInstance(basicBaseDao, configurableFactory);
+                return (T) daoProxyFactory.newInstance(basicBaseDao, configurableFactory,proxyDataProcess);
             } catch (Exception var5) {
                 throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
             }

+ 93 - 0
elab-db/src/main/java/com/elab/core/spring/common/SqlInfoModel.java

@@ -0,0 +1,93 @@
+package com.elab.core.spring.common;
+
+import java.lang.reflect.Type;
+
+/**
+ * SQL的详情信息
+ *
+ * @author : liukx
+ * @time : 2020/6/9 - 15:20
+ */
+public class SqlInfoModel {
+
+    private String tableName;
+
+    private Object args;
+
+    private String sql;
+
+    private Class<?> returnType;
+
+    private Class<?> returnClass;
+
+    private Type mapType;
+
+    private String sqlId;
+
+    private TypeGroupModel paramGroup;
+
+    public Type getMapType() {
+        return mapType;
+    }
+
+    public void setMapType(Type mapType) {
+        this.mapType = mapType;
+    }
+
+    public TypeGroupModel getParamGroup() {
+        return paramGroup;
+    }
+
+    public void setParamGroup(TypeGroupModel paramGroup) {
+        this.paramGroup = paramGroup;
+    }
+
+    public Class<?> getReturnClass() {
+        return returnClass;
+    }
+
+    public void setReturnClass(Class<?> returnClass) {
+        this.returnClass = returnClass;
+    }
+
+    public String getSql() {
+        return sql;
+    }
+
+    public void setSql(String sql) {
+        this.sql = sql;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+    public Object getArgs() {
+        return args;
+    }
+
+    public void setArgs(Object args) {
+        this.args = args;
+    }
+
+
+    public Class<?> getReturnType() {
+        return returnType;
+    }
+
+    public void setReturnType(Class<?> returnType) {
+        this.returnType = returnType;
+    }
+
+    public String getSqlId() {
+        return sqlId;
+    }
+
+    public void setSqlId(String sqlId) {
+        this.sqlId = sqlId;
+    }
+}

+ 9 - 3
elab-db/src/main/java/com/elab/core/spring/common/utils/BeanUtils.java

@@ -8,6 +8,7 @@ import org.apache.commons.logging.LogFactory;
 import java.lang.reflect.*;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 扩展Apache Commons BeanUtils, 提供一些反射方面缺失功能的封装.
@@ -375,12 +376,17 @@ public class BeanUtils extends org.apache.commons.beanutils.BeanUtils {
      * @return
      */
     public static Class getClassGenricType(String className) {
-        int startIdnex = className.indexOf("<") + 1;
+
+        if (className != null && className.startsWith("java.util.Map")) {
+            return Map.class;
+        }
+
+        int startIndex = className.indexOf("<") + 1;
         int endIndex = className.indexOf(">");
-        if (startIdnex == -1 || endIndex == -1) {
+        if (startIndex == -1 || endIndex == -1) {
             return null;
         }
-        String classType = className.substring(startIdnex, endIndex);
+        String classType = className.substring(startIndex, endIndex);
         // 排除为null和构造参数不确定的情况  如 T , ?
         if (StringUtils.isEmpty() || classType.length() == 1) {
             return null;

+ 8 - 1
elab-db/src/main/java/com/elab/core/spring/facotry/DaoFactoryBean.java

@@ -2,6 +2,7 @@ package com.elab.core.spring.facotry;
 
 import com.elab.core.dao.BasicBaseDao;
 import com.elab.core.spring.binding.DaoRegister;
+import com.elab.core.spring.method.ProxyDataProcess;
 import com.elab.core.sql.ConfigurableFactory;
 import org.springframework.beans.factory.FactoryBean;
 
@@ -26,6 +27,8 @@ public class DaoFactoryBean<T> implements FactoryBean<T> {
     // dao注册工厂
     private DaoRegister daoRegister;
 
+    private ProxyDataProcess proxyDataProcess;
+
     private ConfigurableFactory configurableFactory;
 
     public ConfigurableFactory getConfigurableFactory() {
@@ -61,6 +64,10 @@ public class DaoFactoryBean<T> implements FactoryBean<T> {
         this.basicBaseDao = basicBaseDao;
     }
 
+    public void setProxyDataProcess(ProxyDataProcess proxyDataProcess) {
+        this.proxyDataProcess = proxyDataProcess;
+    }
+
     /**
      * beanDefinition中的beanClass只要是这个工厂类的,就会从这里开始调用
      *
@@ -69,7 +76,7 @@ public class DaoFactoryBean<T> implements FactoryBean<T> {
      */
     @Override
     public T getObject() throws Exception {
-        return (T) daoRegister.getDao(mapperInterface, basicBaseDao, configurableFactory);
+        return (T) daoRegister.getDao(mapperInterface, basicBaseDao, configurableFactory, proxyDataProcess);
     }
 
     /**

+ 42 - 0
elab-db/src/main/java/com/elab/core/spring/method/BatchPreparedStatementCreator.java

@@ -0,0 +1,42 @@
+package com.elab.core.spring.method;
+
+import org.springframework.jdbc.core.PreparedStatementCreator;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * 为每个结果集加上Statement.RETURN_GENERATED_KEYS参数
+ *
+ * @author : liukx
+ * @time : 2020/5/13 - 13:32
+ */
+public class BatchPreparedStatementCreator implements PreparedStatementCreator {
+    private String[] generatedKeysColumnNames;
+    private boolean returnGeneratedKeys = true;
+    private String actualSql;
+
+    public BatchPreparedStatementCreator(String actualSql) {
+        this.actualSql = actualSql;
+    }
+
+    public void setGeneratedKeysColumnNames(String[] generatedKeysColumnNames) {
+        this.generatedKeysColumnNames = generatedKeysColumnNames;
+    }
+
+    public void setReturnGeneratedKeys(boolean returnGeneratedKeys) {
+        this.returnGeneratedKeys = returnGeneratedKeys;
+    }
+
+    @Override
+    public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
+        PreparedStatement ps;
+        if (generatedKeysColumnNames != null) {
+            ps = con.prepareStatement(this.actualSql, generatedKeysColumnNames);
+        } else {
+            ps = con.prepareStatement(this.actualSql, PreparedStatement.RETURN_GENERATED_KEYS);
+        }
+        return ps;
+    }
+}

+ 22 - 0
elab-db/src/main/java/com/elab/core/spring/method/DefaultProxyDataProcess.java

@@ -0,0 +1,22 @@
+package com.elab.core.spring.method;
+
+import com.elab.core.spring.common.SqlInfoModel;
+
+/**
+ * 默认的SQL代理器
+ *
+ * @author : liukx
+ * @time : 2020/6/8 - 15:52
+ */
+public class DefaultProxyDataProcess implements ProxyDataProcess {
+    @Override
+    public Object isProxy(SqlInfoModel arg) throws Exception {
+        return null;
+    }
+
+    @Override
+    public <T> Object proxyQueryData(Object proxyData, SqlInfoModel obj) throws Exception {
+        return null;
+    }
+
+}

+ 1 - 1
elab-db/src/main/java/com/elab/core/spring/method/DefaultSQLBuilderSupport.java

@@ -78,7 +78,7 @@ public class DefaultSQLBuilderSupport implements ISQLBuliderSupport {
         String id = entityOperation.pkField.getName();
         StringBuffer sb = new StringBuffer();
         // 这里默认会将主键放在第一位
-        sb.append(" select " + allColumn + " from " + tableName + " where " + id + " = ? ");
+        sb.append(" select " + allColumn + " from " + tableName + " where " + id + " = :id ");
         return sb.toString();
     }
 

+ 30 - 0
elab-db/src/main/java/com/elab/core/spring/method/ProxyDataProcess.java

@@ -0,0 +1,30 @@
+package com.elab.core.spring.method;
+
+import com.elab.core.spring.common.SqlInfoModel;
+
+/**
+ * 代理SQL执行器
+ *
+ * @author : liukx
+ * @time : 2020/6/8 - 15:47
+ */
+public interface ProxyDataProcess {
+    /**
+     * 是否满足代理请求的条件
+     *
+     * @param arg
+     * @return 如果不为null则会触发下面的proxyData方法, 为null不做处理
+     */
+    public Object isProxy(SqlInfoModel arg) throws Exception;
+
+    /**
+     * 执行代理查询数据的操作
+     *
+     * @param <T>
+     * @param proxyData 允许代理的代理对象
+     * @param obj       SQL关联请求参数
+     * @return
+     */
+    public <T> Object proxyQueryData(Object proxyData, SqlInfoModel obj) throws Exception;
+
+}

+ 3 - 2
elab-db/src/main/java/com/elab/core/spring/method/SQLEventHandler.java

@@ -10,8 +10,9 @@ import com.elab.core.sql.config.SqlCommandType;
  */
 public interface SQLEventHandler {
 
-    public void sqlInvokeBefore(SqlCommandType sqlCommandType, String sql, Object params);
+    public void sqlInvokeBefore(SqlCommandType sqlCommandType, String sqlId, String table, String sql, Object params);
 
-    public void sqlInvokeAfter(SqlCommandType sqlCommandType, String sql, Object params, Object result);
+    public void sqlInvokeAfter(SqlCommandType sqlCommandType, String sqlId, String table, String sql, Object params,
+                               Object result);
 
 }

+ 4 - 2
elab-db/src/main/java/com/elab/core/spring/method/SQLEventHandlerAdaptor.java

@@ -10,13 +10,15 @@ import com.elab.core.sql.config.SqlCommandType;
  */
 public class SQLEventHandlerAdaptor implements SQLEventHandler {
 
+
     @Override
-    public void sqlInvokeBefore(SqlCommandType sqlCommandType, String sql, Object params) {
+    public void sqlInvokeBefore(SqlCommandType sqlCommandType, String sqlId, String table, String sql, Object params) {
 
     }
 
     @Override
-    public void sqlInvokeAfter(SqlCommandType sqlCommandType, String sql, Object params, Object result) {
+    public void sqlInvokeAfter(SqlCommandType sqlCommandType, String sqlId, String table, String sql, Object params,
+                               Object result) {
 
     }
 }

+ 19 - 0
elab-db/src/main/java/com/elab/core/sql/config/SQLQueryType.java

@@ -0,0 +1,19 @@
+package com.elab.core.sql.config;
+
+/**
+ * SQL的查询类型
+ *
+ * @author : liukx
+ * @time : 2020/6/9 - 14:52
+ */
+public enum SQLQueryType {
+    UNKNOWN,
+    Object,
+    Map,
+    List,
+    Page,
+    Count;
+
+    private SQLQueryType() {
+    }
+}

+ 40 - 2
elab-db/src/test/java/com.db.service/main/BasicBaseDaoCase.java

@@ -5,6 +5,7 @@ import com.db.service.model.TTest;
 import com.elab.core.dao.BasicBaseDao;
 import com.elab.core.dao.model.JdbcParamsModel;
 import com.elab.core.sql.ConfigurableFactory;
+import com.elab.core.sql.config.SqlCommandType;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -12,6 +13,9 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -93,6 +97,42 @@ public class BasicBaseDaoCase {
         System.out.println(integer);
     }
 
+    @Test
+    public void testBatchInsert() throws Exception {
+        List<TTest> list = new ArrayList<>();
+        for (int i = 0; i < 10; i++) {
+            TTest test = new TTest();
+            test.setStatus("1");
+            test.setUsername("batchTest_" + i);
+            test.setSex("Man");
+            test.setTestId("bbbb");
+            test.setTime(new Date());
+            test.setCreated(new Date());
+            test.setName("batchTest_name_" + i);
+//            test.setLoveName("aaaaa");
+            list.add(test);
+        }
+        String sql = "insert into t_test (username, name, sex, status, created, time, test_id, love_name) values " +
+                "(:username, :name, :sex, :status, :created, :time, :test_id, :love_name)";
+//        String sql = "insert into t_test (username, name, sex, status, created, time, test_id, love_name) values (?, ?, ?, ?, ?, ?, ?, ?)";
+        basicBaseDao.batchOperation(SqlCommandType.INSERT, sql, list);
+        System.out.println("=========");
+    }
+
+    @Test
+    public void testBatchUpdate() throws Exception {
+        List<TTest> list = new ArrayList<>();
+        for (int i = 1; i < 10; i++) {
+            TTest test = new TTest();
+            test.setTime(new Date());
+            test.setId(i);
+            list.add(test);
+        }
+        String sql = "update t_test set time = :time where id = :id";
+//        String sql = "insert into t_test (username, name, sex, status, created, time, test_id, love_name) values (?, ?, ?, ?, ?, ?, ?, ?)";
+        basicBaseDao.batchOperation(SqlCommandType.UPDATE, sql, list);
+    }
+
     @Test
     public void testSql() throws Exception {
         String json = "{\"houseId\":10151,\"writeType\":1,\"keyword1\":\"%%\",\"keyword2\n" +
@@ -120,6 +160,4 @@ public class BasicBaseDaoCase {
         JdbcParamsModel model = basicBaseDao.commonParseSql(sql, map);
         Assert.assertTrue(model.getSql().equals(okSql));
     }
-
-
 }

+ 1 - 0
elab-db/src/test/java/com.db.service/main/BasicBaseDaoProxyCase.java

@@ -43,6 +43,7 @@ public class BasicBaseDaoProxyCase {
     @Test
     public void testInsertCase() throws Exception {
         TTest test = new TTest();
+        test.setId(1099);
         test.setStatus("1");
         test.setUsername("某某某xx111");
 //        test.setTestId("123123");

+ 1 - 2
elab-db/src/test/java/com.db.service/model/HelloDTO.java

@@ -1,7 +1,6 @@
 package com.db.service.model;
 
 
-import com.elab.annotation.db.Column;
 
 /**
  * @author liuhx on 2017/4/5 11:28
@@ -13,7 +12,7 @@ public class HelloDTO  {
     private String name;
     private String value;
     private String type;
-    @Column(name="parentcode")
+    @javax.persistence.Column(name="parentcode")
     private String code;
     private String remark;
     private String status;

+ 2 - 2
elab-es/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>elab-parent</artifactId>
         <groupId>com.elab.core</groupId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -46,7 +46,7 @@
         </dependency>
         <!--        <dependency>-->
         <!--            <groupId>org.springframework.boot</groupId>-->
-        <!--            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>-->
+        <!--            <artifactId>spring-boot-redisson-data-elasticsearch</artifactId>-->
         <!--            <version>2.1.6.RELEASE</version>-->
         <!--        </dependency>-->
         <!--        <dependency>-->

+ 1 - 1
elab-es/src/main/resources/META-INF/spring.factories

@@ -1 +1 @@
-#org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.elab.es.client.configuration.ESConfiguration
+#org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.elab.es.client.doc.ESConfiguration

+ 4 - 4
elab-log/pom.xml

@@ -7,7 +7,7 @@
     <parent>
         <groupId>com.elab.core</groupId>
         <artifactId>elab-parent</artifactId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
 
     <groupId>com.elab.log</groupId>
@@ -52,7 +52,7 @@
         </dependency>
         <!--<dependency>-->
             <!--<groupId>org.springframework.cloud</groupId>-->
-            <!--<artifactId>spring-cloud-starter-feign</artifactId>-->
+            <!--<artifactId>spring-cloud-redisson-feign</artifactId>-->
             <!--<version>1.4.4.RELEASE</version>-->
             <!--<scope>provided</scope>-->
         <!--</dependency>-->
@@ -97,12 +97,12 @@
                         <!--<goals>-->
                             <!--<goal>jar</goal>-->
                         <!--</goals>-->
-                        <!--<configuration>-->
+                        <!--<doc>-->
                             <!--<additionalparam>-Xdoclint:none</additionalparam>-->
                             <!--<charset>UTF-8</charset>-->
                             <!--<encoding>UTF-8</encoding>-->
                             <!--<docencoding>UTF-8</docencoding>-->
-                        <!--</configuration>-->
+                        <!--</doc>-->
                     <!--</execution>-->
                 <!--</executions>-->
             <!--</plugin>-->

+ 6 - 6
elab-log/src/main/java/com/elab/log/asepct/CatAspect.java

@@ -60,7 +60,7 @@ public class CatAspect {
             }
         }
         Transaction t = Cat.newTransaction(value, fullMethodName);
-        logger.info(" 开始执行方法 " + method.getName() + " 参数:" + StringUtils.logOut(JSON.toJSONString(pjp.getArgs())));
+        logger.debug(" 开始执行方法 " + method.getName() + " 参数:" + StringUtils.logOut(JSON.toJSONString(pjp.getArgs())));
         try {
             proceed = pjp.proceed();
             t.setStatus(Transaction.SUCCESS);
@@ -81,13 +81,13 @@ public class CatAspect {
                     info.setExtension(extension);
                 }
                 info.getExtension().put("messageId", currentMessageId);
-                logger.info("出参结果 : " + info.isSuccess() + " 消息内容 : " + info.getMessage());
+                logger.debug("出参结果 : " + info.isSuccess() + " 消息内容 : " + info.getMessage());
             }
-            logger.info(" 结束执行方法 " + method.getName());
+            logger.debug(" 结束执行方法 " + method.getName());
             long start = System.currentTimeMillis();
             t.complete();
             long time = System.currentTimeMillis() - start;
-            logger.info(" CAT Service 拦截器事物提交时间 : " + time);
+            logger.debug(" CAT Service 拦截器事物提交时间 : " + time);
         }
         return proceed;
     }
@@ -99,11 +99,11 @@ public class CatAspect {
      */
     private void exceptionProcess(Throwable e) {
         if (e instanceof CoreException) {
-            logger.info(e.getMessage());
+            logger.debug(e.getMessage());
             throw new CoreException(((CoreException) e).getErrorCode(), e.getMessage());
         }
         if (e instanceof BusinessException) {
-            logger.info(e.getMessage());
+            logger.debug(e.getMessage());
             throw new BusinessException(((BusinessException) e).getErrorCode(), e.getMessage());
         }
     }

+ 1 - 1
elab-log/src/main/java/com/elab/log/asepct/CatDaoAscept.java

@@ -47,7 +47,7 @@ public class CatDaoAscept {
             long start = System.currentTimeMillis();
             transaction.complete();
             long time = System.currentTimeMillis() - start;
-            logger.info(" CAT DAO 拦截器事物提交时间 : " + time);
+            logger.debug(" CAT DAO 拦截器事物提交时间 : " + time);
         }
         return proceed;
     }

+ 1 - 1
elab-log/src/main/java/com/elab/log/asepct/ExceptionAspect.java

@@ -6,7 +6,7 @@
 //import com.elab.core.bean.BaseInfo;
 //import com.elab.core.bean.Info;
 //import com.elab.core.collections.NameValueCollection;
-//import com.elab.core.configuration.ConfigManager;
+//import com.elab.core.doc.ConfigManager;
 //import com.elab.core.exception.CoreException;
 //import com.elab.core.log.LogEntry;
 //import com.elab.core.log.LogLevel;

+ 1 - 1
elab-log/src/main/java/com/elab/log/asepct/FeignInterceptor.java

@@ -39,7 +39,7 @@
 //        requestTemplate.header(Cat.Context.CHILD, childId);
 //        requestTemplate.header(Cat.Context.PARENT, parentId);
 //        requestTemplate.header(CatMsgConstants.APPLICATION_KEY, Cat.getManager().getDomain());
-//        logger.info(" 开始Feign远程调用 : 请求路径 : " + requestTemplate.url() + " 请求类型 : " + requestTemplate.method());
+//        logger.debug(" 开始Feign远程调用 : 请求路径 : " + requestTemplate.url() + " 请求类型 : " + requestTemplate.method());
 //    }
 //
 //

+ 1 - 1
elab-log/src/main/java/com/elab/log/strategy/FeignHystrixConcurrencyStrategy.java

@@ -56,7 +56,7 @@
 //    private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
 //                                                 HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) {
 //        if (log.isDebugEnabled()) {
-//            log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
+//            log.debug("Current Hystrix plugins doc is [" + "concurrencyStrategy ["
 //                    + this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
 //                    + metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
 //            log.debug("Registering Sleuth Hystrix Concurrency Strategy.");

+ 37 - 0
elab-log/src/main/java/com/elab/log/utils/CatCrossProcess.java

@@ -8,6 +8,8 @@ import org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.RequestContextHolder;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
 /**
  * @author : liukx
@@ -73,4 +75,39 @@ public class CatCrossProcess {
         requestAttributes.setAttribute(Cat.Context.CHILD, context.getProperty(Cat.Context.CHILD), 0);
         requestAttributes.setAttribute(CatMsgConstants.APPLICATION_KEY, Cat.getManager().getDomain(), 0);
     }
+
+    /**
+     * 获取消息上下文对象
+     *
+     * @return
+     */
+    public static Map<String, String> getMsgContextMap() {
+        CatMsgContext context = new CatMsgContext();
+        Cat.logRemoteCallClient(context);
+        Map<String, String> contextMap = new LinkedHashMap<>();
+        contextMap.put(Cat.Context.PARENT, context.getProperty(Cat.Context.PARENT));
+        contextMap.put(Cat.Context.ROOT, context.getProperty(Cat.Context.ROOT));
+        contextMap.put(Cat.Context.CHILD, context.getProperty(Cat.Context.CHILD));
+        contextMap.put(CatMsgConstants.APPLICATION_KEY, Cat.getManager().getDomain());
+        return contextMap;
+    }
+
+    public static Transaction buildCrossMsg(String crossName, String crossValue, Map<String, String> msgIdMap) {
+        Transaction transaction = Cat.newTransaction(crossName, crossValue);
+
+        if (msgIdMap != null && msgIdMap.get(Cat.Context.ROOT) != null) {
+            Cat.Context context = new CatMsgContext();
+            context.addProperty(Cat.Context.ROOT, msgIdMap.get(Cat.Context.ROOT));
+            context.addProperty(Cat.Context.PARENT, msgIdMap.get(Cat.Context.PARENT));
+            context.addProperty(Cat.Context.CHILD, msgIdMap.get(Cat.Context.CHILD));
+            Cat.logRemoteCallServer(context);
+
+            Event crossAppEvent = Cat.newEvent(CatMsgConstants.PROVIDER_CALL_APP, msgIdMap.get(CatMsgConstants.APPLICATION_KEY));
+            crossAppEvent.setStatus(Event.SUCCESS);
+            transaction.addChild(crossAppEvent);
+        }
+
+
+        return transaction;
+    }
 }

+ 3 - 0
elab-log/src/main/java/com/elab/log/utils/CatMsgConstants.java

@@ -18,6 +18,9 @@ public class CatMsgConstants {
      */
     public static final String CROSS_SERVER = "PigeonService";
 
+    public static final String CROSS_MQ = "PigeonMQ";
+
+
     public static final String PROVIDER_APPLICATION_NAME = "serverApplicationName";
 
     public static final String CONSUMER_CALL_SERVER = "PigeonCall.server";

+ 1 - 1
elab-mongodb/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>elab-parent</artifactId>
         <groupId>com.elab.core</groupId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 4 - 4
elab-mq/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>elab-parent</artifactId>
         <groupId>com.elab.core</groupId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -35,7 +35,7 @@
         <dependency>
             <groupId>com.aliyun.openservices</groupId>
             <artifactId>ons-client</artifactId>
-            <version>1.8.0.Final</version>
+            <version>1.8.4.Final</version>
         </dependency>
         <dependency>
             <groupId>junit</groupId>
@@ -75,9 +75,9 @@
                         <!--<goals>-->
                             <!--<goal>jar</goal>-->
                         <!--</goals>-->
-                        <!--<configuration>-->
+                        <!--<doc>-->
                             <!--<additionalOptions>-Xdoclint:none</additionalOptions>-->
-                        <!--</configuration>-->
+                        <!--</doc>-->
                     <!--</execution>-->
                 <!--</executions>-->
             <!--</plugin>-->

+ 33 - 14
elab-mq/src/main/java/com/elab/mq/listener/AbstractMessageListener.java

@@ -7,7 +7,7 @@ import com.aliyun.openservices.ons.api.MessageListener;
 import com.dianping.cat.Cat;
 import com.dianping.cat.message.Transaction;
 import com.elab.core.utils.DateUtils;
-import com.elab.core.utils.ObjectUtils;
+import com.elab.log.utils.CatCrossProcess;
 import com.elab.mq.dao.IConsumerDao;
 import com.elab.mq.model.ConsumerEntity;
 import com.elab.mq.model.MessageModel;
@@ -16,7 +16,9 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 
-import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
 
 /**
  * 抽象成客户端能够使用的方法
@@ -123,7 +125,12 @@ public abstract class AbstractMessageListener implements MessageListener {
     public Action consume(Message message, ConsumeContext consumeContext) {
         String topic = message.getTopic();
         String tag = message.getTag();
-        Transaction t = Cat.newTransaction(MQ_CONSUMER, topic + "_" + tag + "_" + getClass().getSimpleName());
+
+
+        Map<String, String> msgMap = conversionPropertyToMap(message.getUserProperties());
+
+        Transaction t = CatCrossProcess.buildCrossMsg(MQ_CONSUMER, topic + "_" + tag + "_" + getClass().getSimpleName(), msgMap);
+
         MessageModel messageModel = new MessageModel(message);
         logger.debug("消息处理被触发 : " + message.toString());
         Action action = null;
@@ -136,18 +143,18 @@ public abstract class AbstractMessageListener implements MessageListener {
                 return Action.CommitMessage;
             }
 
-            logger.debug("更新消费者数据: " + ObjectUtils.objectParseJsonStr(oldConsumerEntity));
+            //logger.debug("更新消费者数据: " + ObjectUtils.objectParseJsonStr(oldConsumerEntity));
             action = consume0(messageModel, consumeContext);
-
-            if (action == null || Action.ReconsumeLater.equals(action)) {
-                oldConsumerEntity.setConsumerStatus(-1);
-                oldConsumerEntity.setUpdated(new Date());
-                consumerDao.updateById(oldConsumerEntity);
-            } else {
-                oldConsumerEntity.setConsumerStatus(1);
-                oldConsumerEntity.setUpdated(new Date());
-                consumerDao.updateById(oldConsumerEntity);
-            }
+            // 锁争抢太严重了。
+//            if (action == null || Action.ReconsumeLater.equals(action)) {
+//                oldConsumerEntity.setConsumerStatus(-1);
+//                oldConsumerEntity.setUpdated(new Date());
+//                consumerDao.updateById(oldConsumerEntity);
+//            } else {
+//                oldConsumerEntity.setConsumerStatus(1);
+//                oldConsumerEntity.setUpdated(new Date());
+//                consumerDao.updateById(oldConsumerEntity);
+//            }
             t.setSuccessStatus();
         } catch (Exception e) {
             t.setStatus(e);
@@ -161,4 +168,16 @@ public abstract class AbstractMessageListener implements MessageListener {
         return action;
     }
 
+    private Map<String, String> conversionPropertyToMap(Properties userProperties) {
+        if (userProperties == null) {
+            return null;
+        }
+
+        Map<String, String> msgMap = new LinkedHashMap<>();
+        userProperties.stringPropertyNames().forEach((key) -> {
+            msgMap.put(key, userProperties.getProperty(key));
+        });
+        return msgMap;
+    }
+
 }

+ 11 - 0
elab-mq/src/main/java/com/elab/mq/msg/IMsgProducerFacade.java

@@ -6,6 +6,7 @@ import com.elab.mq.msg.adptor.SendCallbackAdaptor;
 
 import java.util.Date;
 import java.util.Properties;
+import java.util.concurrent.ExecutorService;
 
 /**
  * 消息生产门面类
@@ -66,4 +67,14 @@ public interface IMsgProducerFacade {
      * @param properties
      */
     public void setProperties(Properties properties);
+
+    /**
+     * 设置异步发送消息执行Callback的目标线程池。
+     *
+     * 如果不设置,将使用公共线程池,仅建议执行轻量级的Callback任务,避免阻塞公共线程池
+     * 引起其它链路超时。
+     *
+     * @param callbackExecutor 执行Callback的线程池
+     */
+    public void setCallbackExecutor(final ExecutorService callbackExecutor);
 }

+ 17 - 2
elab-mq/src/main/java/com/elab/mq/msg/impl/MsgProducerImpl.java

@@ -7,6 +7,7 @@ import com.dianping.cat.message.Transaction;
 import com.elab.core.utils.DateUtils;
 import com.elab.core.utils.ObjectUtils;
 import com.elab.core.utils.StringUtils;
+import com.elab.log.utils.CatCrossProcess;
 import com.elab.mq.dao.IProducerDao;
 import com.elab.mq.model.MessageModel;
 import com.elab.mq.model.ProducerEntity;
@@ -19,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 
 import java.util.Date;
+import java.util.Map;
 import java.util.Properties;
 
 /**
@@ -115,22 +117,31 @@ public class MsgProducerImpl extends ProducerBean implements IMsgProducerFacade
         logger.debug(" 发送一条消息 " + message.getMsgID() + " -> " + message.toString());
         int id = insertRecordProducer(message);
         try {
+            catTraceId(message);
             message.setProducerId(id + "");
             SendResult result = super.send(message);
-            updateRecordProducer(id, 1, result.toString());
+            //updateRecordProducer(id, 1, result.toString());
             logger.debug(" 消息发送结果 : " + result.toString());
             t.setSuccessStatus();
             return new SendResultModel(result);
         } catch (Exception e) {
             logger.error("消息发送异常", e);
             t.setStatus(e);
-            updateRecordProducer(id, -1, e.getMessage());
+            //updateRecordProducer(id, -1, e.getMessage());
         } finally {
             t.complete();
         }
         return null;
     }
 
+    private void catTraceId(MessageModel message) {
+        // 植入Cat链路编号
+        Map<String, String> msgContextMap = CatCrossProcess.getMsgContextMap();
+        msgContextMap.forEach((K, V) -> {
+            message.putUserProperties(K, V);
+        });
+    }
+
     @Override
     public void sendOneway(MessageModel message) {
         logger.debug(" 发送一条单向消息 " + message.getMsgID() + " -> " + message.toString());
@@ -156,4 +167,8 @@ public class MsgProducerImpl extends ProducerBean implements IMsgProducerFacade
     public void setProperties(Properties properties) {
         super.setProperties(properties);
     }
+
+
+
+
 }

+ 44 - 0
elab-redis/pom.xml

@@ -0,0 +1,44 @@
+<?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>elab-parent</artifactId>
+        <groupId>com.elab.core</groupId>
+        <version>2.0.4.14-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>elab-redis</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson-spring-boot-starter</artifactId>
+            <version>3.8.2</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <version>2.0.1.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.elab.core</groupId>
+            <artifactId>elab-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-http</artifactId>
+            <version>5.0.7</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 62 - 0
elab-redis/src/main/java/com/elab/redis/CacheTemplate.java

@@ -0,0 +1,62 @@
+package com.elab.redis;
+
+import org.redisson.api.RedissonClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.redis.core.*;
+
+/**
+ * 缓存直接操作类
+ *
+ * @author : liukx
+ * @time : 2020/7/8 - 16:53
+ */
+public class CacheTemplate {
+
+    private Logger logger = LoggerFactory.getLogger(CacheTemplate.class);
+
+    private RedissonClient redissonClient;
+
+    private Long timeOut;
+
+    private RedisTemplate<Object, Object> redisTemplate;
+
+
+    public ValueOperations string() {
+        return redisTemplate.opsForValue();
+    }
+
+    public RedissonClient getRedissonClient() {
+        return redissonClient;
+    }
+
+    public void setRedissonClient(RedissonClient redissonClient) {
+        this.redissonClient = redissonClient;
+    }
+
+    public RedisTemplate<Object, Object> getRedisTemplate() {
+        return redisTemplate;
+    }
+
+    public void setRedisTemplate(RedisTemplate<Object, Object> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    public HashOperations map() {
+        return redisTemplate.opsForHash();
+    }
+
+    public ListOperations list() {
+        return redisTemplate.opsForList();
+    }
+
+    public SetOperations set() {
+        return redisTemplate.opsForSet();
+    }
+
+    public ZSetOperations zset() {
+        return redisTemplate.opsForZSet();
+    }
+
+}
+

+ 44 - 0
elab-redis/src/main/java/com/elab/redis/annotation/CacheLoopSubmit.java

@@ -0,0 +1,44 @@
+package com.elab.redis.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface CacheLoopSubmit {
+
+    /**
+     * 缓存的key
+     *
+     * @return
+     */
+    String cacheName() default "";
+
+
+    /**
+     * 重复提交的标识key.
+     * ALL如果是实体对象那么,对比所有属性。
+     * 建议使用者重写实体的equals方法。
+     * 如果是原始数据类型,包含String等等,则填写需要对比的对象下标 {0},{1}
+     *
+     * @return
+     */
+    String[] unionKey() default {};
+
+    /**
+     * 锁超时时间(秒)
+     *
+     * @return
+     */
+    int timeOut() default 5;
+
+    /**
+     * 优先级
+     *
+     * @return
+     */
+    int order() default Integer.MAX_VALUE;
+
+
+}

+ 78 - 0
elab-redis/src/main/java/com/elab/redis/config/CacheAutoConfiguration.java

@@ -0,0 +1,78 @@
+package com.elab.redis.config;
+
+import com.elab.redis.CacheTemplate;
+import com.elab.redis.annotation.CacheLoopSubmit;
+import com.elab.redis.interceptor.BeanFactoryCacheAttributeSourceAdvisor;
+import com.elab.redis.interceptor.CacheAttributeSourcePointcut;
+import com.elab.redis.interceptor.CacheInterceptor;
+import com.elab.redis.spring.data.RedisTemplateDecorator;
+import org.redisson.api.RedissonClient;
+import org.redisson.spring.cache.RedissonSpringCacheManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.cache.CacheManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Role;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+
+/**
+ * 缓存自动配置
+ *
+ * @author : liukx
+ * @time : 2020/7/8 - 19:12
+ */
+@Configuration
+@ComponentScan(value = {"com.elab.redis.interceptor.impl"})
+public class CacheAutoConfiguration {
+
+
+    @Bean
+    public CacheTemplate cacheTemplate(@Autowired RedissonClient redissonClient, @Autowired RedisTemplate<Object,
+            Object> redisTemplate) {
+        CacheTemplate cacheTemplate = new CacheTemplate();
+        cacheTemplate.setRedissonClient(redissonClient);
+        cacheTemplate.setRedisTemplate(redisTemplate);
+        return cacheTemplate;
+    }
+
+    @Bean
+    public RedisTemplate<Object, Object> redisTemplate(
+            RedisConnectionFactory redisConnectionFactory) throws Exception {
+        RedisTemplate<Object, Object> template = new RedisTemplateDecorator<>();
+        template.setConnectionFactory(redisConnectionFactory);
+        return template;
+    }
+
+    @Bean
+    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+    public BeanFactoryCacheAttributeSourceAdvisor transactionAdvisor() {
+        BeanFactoryCacheAttributeSourceAdvisor advisor = new BeanFactoryCacheAttributeSourceAdvisor();
+        advisor.setAdvice(transactionInterceptor());
+        advisor.setPointcut(cacheAttributeSourcePointcut());
+        return advisor;
+    }
+
+    @Bean
+    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+    public CacheAttributeSourcePointcut cacheAttributeSourcePointcut() {
+        CacheAttributeSourcePointcut cacheAttributeSourcePointcut = new CacheAttributeSourcePointcut();
+        cacheAttributeSourcePointcut.addAnnotations(CacheLoopSubmit.class);
+        return cacheAttributeSourcePointcut;
+    }
+
+    @Bean
+    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+    public CacheInterceptor transactionInterceptor() {
+        CacheInterceptor interceptor = new CacheInterceptor();
+        return interceptor;
+    }
+
+    @Bean
+    CacheManager cacheManager(RedissonClient redissonClient) {
+        return new RedissonSpringCacheManager(redissonClient);
+    }
+
+}

+ 14 - 0
elab-redis/src/main/java/com/elab/redis/consts/CacheConstants.java

@@ -0,0 +1,14 @@
+package com.elab.redis.consts;
+
+/**
+ * 常量定义
+ *
+ * @author : liukx
+ * @time : 2020/7/10 - 14:16
+ */
+public class CacheConstants {
+
+
+    public final static String ALL = "ALL";
+
+}

+ 24 - 0
elab-redis/src/main/java/com/elab/redis/interceptor/BeanFactoryCacheAttributeSourceAdvisor.java

@@ -0,0 +1,24 @@
+package com.elab.redis.interceptor;
+
+import org.springframework.aop.Pointcut;
+import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
+
+/**
+ * 重复提交
+ *
+ * @author : liukx
+ * @time : 2020/7/9 - 19:55
+ */
+public class BeanFactoryCacheAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
+
+    private CacheAttributeSourcePointcut pointcut;
+
+    public void setPointcut(CacheAttributeSourcePointcut pointcut) {
+        this.pointcut = pointcut;
+    }
+
+    @Override
+    public Pointcut getPointcut() {
+        return pointcut;
+    }
+}

+ 32 - 0
elab-redis/src/main/java/com/elab/redis/interceptor/CacheAttributeSourceAdvisor.java

@@ -0,0 +1,32 @@
+package com.elab.redis.interceptor;
+
+import org.aopalliance.aop.Advice;
+import org.springframework.aop.Pointcut;
+import org.springframework.aop.support.AbstractPointcutAdvisor;
+import org.springframework.util.Assert;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 19:59
+ */
+public class CacheAttributeSourceAdvisor extends AbstractPointcutAdvisor {
+
+    private CacheInterceptor cacheInterceptor;
+
+    private CacheAttributeSourcePointcut pointcut;
+
+    public void setCacheInterceptor(CacheInterceptor cacheInterceptor) {
+        this.cacheInterceptor = cacheInterceptor;
+    }
+
+    @Override
+    public Pointcut getPointcut() {
+        return pointcut;
+    }
+
+    @Override
+    public Advice getAdvice() {
+        Assert.state(this.cacheInterceptor != null, "No cacheInterceptor set");
+        return cacheInterceptor;
+    }
+}

+ 31 - 0
elab-redis/src/main/java/com/elab/redis/interceptor/CacheAttributeSourcePointcut.java

@@ -0,0 +1,31 @@
+package com.elab.redis.interceptor;
+
+import com.elab.redis.utils.CacheParseUtil;
+import org.springframework.aop.support.StaticMethodMatcherPointcut;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 20:02
+ */
+public class CacheAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
+
+    private Set<Class<? extends Annotation>> CACHE_OPERATION_ANNOTATIONS = new LinkedHashSet<>(8);
+
+    public void addAnnotations(Class<? extends Annotation> annotation) {
+        CACHE_OPERATION_ANNOTATIONS.add(annotation);
+    }
+
+    @Override
+    public boolean matches(Method method, Class<?> targetClass) {
+        if (CacheParseUtil.isContainAnnotations(CACHE_OPERATION_ANNOTATIONS, method)) {
+            return true;
+        }
+        return false;
+    }
+}

+ 36 - 0
elab-redis/src/main/java/com/elab/redis/interceptor/CacheInterceptor.java

@@ -0,0 +1,36 @@
+package com.elab.redis.interceptor;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.aop.framework.ReflectiveMethodInvocation;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 20:08
+ */
+public class CacheInterceptor implements MethodInterceptor, Serializable {
+    @Autowired
+    private List<ICacheProcessService> cacheProcessServices;
+
+    @Override
+    public Object invoke(MethodInvocation invocation) throws Throwable {
+        Object proceed = null;
+        if (invocation instanceof ReflectiveMethodInvocation) {
+            ReflectiveMethodInvocation methodInvocation = (ReflectiveMethodInvocation) invocation;
+            Method method = invocation.getMethod();
+            for (int i = 0; i < cacheProcessServices.size(); i++) {
+                ICacheProcessService cache = cacheProcessServices.get(i);
+                if (cache.subscribe(method)) {
+                    proceed = cache.invokeWithinTransaction(methodInvocation);
+                }
+            }
+            return proceed;
+        }
+        return null;
+    }
+}

+ 42 - 0
elab-redis/src/main/java/com/elab/redis/interceptor/ICacheProcessService.java

@@ -0,0 +1,42 @@
+package com.elab.redis.interceptor;
+
+import org.springframework.aop.framework.ReflectiveMethodInvocation;
+
+import java.lang.reflect.Method;
+
+/**
+ * 定义缓存处理的业务规则
+ *
+ * @author : liukx
+ * @date : 2020/7/10 - 10:50
+ */
+public interface ICacheProcessService {
+    /**
+     * 是否关注指定方法
+     *
+     * @param method
+     * @return
+     */
+    public boolean subscribe(Method method);
+
+    /**
+     * 关注之后,处理的业务逻辑
+     *
+     * @param invocation
+     * @return
+     * @throws Throwable
+     */
+    public Object invokeWithinTransaction(ReflectiveMethodInvocation invocation) throws Throwable;
+
+//    @FunctionalInterface
+//    interface InvocationCallback {
+//        /**
+//         * 具体业务最终执行的方法
+//         *
+//         * @return
+//         * @throws Throwable
+//         */
+//        Object proceedWithInvocation() throws Throwable;
+//
+//    }
+}

+ 114 - 0
elab-redis/src/main/java/com/elab/redis/interceptor/impl/CacheLoopProcessImpl.java

@@ -0,0 +1,114 @@
+package com.elab.redis.interceptor.impl;
+
+import com.elab.redis.annotation.CacheLoopSubmit;
+import com.elab.redis.interceptor.ICacheProcessService;
+import com.elab.redis.utils.CacheParseUtil;
+import org.apache.commons.beanutils.BeanUtils;
+import org.redisson.api.RLock;
+import org.redisson.api.RMap;
+import org.redisson.api.RedissonClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.framework.ReflectiveMethodInvocation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 防重复提交
+ *
+ * @author : liukx
+ * @time : 2020/7/10 - 10:58
+ */
+@Component
+public class CacheLoopProcessImpl implements ICacheProcessService {
+
+    @Autowired
+    private RedissonClient client;
+
+    @Value("${spring.application.name}")
+    private String applicationName;
+
+    private Logger logger = LoggerFactory.getLogger(CacheLoopProcessImpl.class);
+
+    @Override
+    public boolean subscribe(Method method) {
+        return CacheParseUtil.isContainAnnotation(method, CacheLoopSubmit.class);
+    }
+
+    @Override
+    public Object invokeWithinTransaction(ReflectiveMethodInvocation invocation) throws Throwable {
+        Method method = invocation.getMethod();
+        CacheLoopSubmit cacheLoopSubmit = AnnotationUtils.getAnnotation(method, CacheLoopSubmit.class);
+        String[] unionKey = cacheLoopSubmit.unionKey();
+        int timeOut = cacheLoopSubmit.timeOut();
+        String clazzName = method.getDeclaringClass().getName();
+        Object[] arguments = invocation.getArguments();
+
+        String cacheKey = generateUnionKey(unionKey, arguments);
+        String lockKey = applicationName + "|" + clazzName + "." + method.getName();
+
+        RMap<Object, Object> map = client.getMap(lockKey);
+        RLock lock = map.getLock(cacheKey);
+        try {
+            if (lock.tryLock(timeOut, TimeUnit.SECONDS)) {
+                logger.debug("当前线程抢占到锁");
+                Object proceed = invocation.proceed();
+                return proceed;
+            }
+        } catch (InterruptedException e) {
+            logger.warn("尝试获取锁超时", e);
+        } finally {
+            lock.unlock();
+        }
+        return null;
+    }
+
+    /**
+     * 通过key定义的集合到对应的参数里面生成对应的唯一键
+     * 非键值对类型: [0]
+     * 键值对类型: [0]{xxx}
+     *
+     * @param unionKey  唯一key集合
+     * @param arguments 参数集合
+     * @return
+     * @throws Exception
+     */
+    private String generateUnionKey(String[] unionKey, Object[] arguments) throws Exception {
+        StringBuffer unionValue = new StringBuffer();
+        for (int i = 0; i < unionKey.length; i++) {
+            String key = unionKey[i];
+            if (key.startsWith("[")) {
+                String paramIndex = getParamIndex(key, "[", "]");
+                Integer index = Integer.valueOf(paramIndex);
+                Object value = null;
+
+                if (key.indexOf("{") > -1) {
+                    String property = getParamIndex(key, "{", "}");
+                    value = BeanUtils.getProperty(arguments[index], property);
+                } else {
+                    value = arguments[index];
+                }
+
+                if (value == null) {
+                    unionValue.append("_");
+                } else {
+                    unionValue.append(value + "_");
+                }
+            }
+        }
+        return unionValue.toString();
+    }
+
+    public static String getParamIndex(String str, String startSymbol, String endSymbol) {
+        int start = str.indexOf(startSymbol) + 1;
+        int end = str.indexOf(endSymbol, start);
+        String content = str.substring(start, end);
+        return content;
+    }
+
+}

+ 35 - 0
elab-redis/src/main/java/com/elab/redis/model/CacheValueModel.java

@@ -0,0 +1,35 @@
+package com.elab.redis.model;
+
+/**
+ * 缓存值实体
+ *
+ * @author : liukx
+ * @time : 2020/7/8 - 17:55
+ */
+public class CacheValueModel {
+
+    private String v;
+
+    private Class cls;
+
+    public CacheValueModel(String v, Class cls) {
+        this.v = v;
+        this.cls = cls;
+    }
+
+    public String getV() {
+        return v;
+    }
+
+    public void setV(String v) {
+        this.v = v;
+    }
+
+    public Class getCls() {
+        return cls;
+    }
+
+    public void setCls(Class cls) {
+        this.cls = cls;
+    }
+}

+ 30 - 0
elab-redis/src/main/java/com/elab/redis/model/annotation/LoopSubmitModel.java

@@ -0,0 +1,30 @@
+package com.elab.redis.model.annotation;
+
+/**
+ * 重复提交注解映射实体
+ *
+ * @author : liukx
+ * @time : 2020/7/9 - 19:51
+ */
+public class LoopSubmitModel {
+
+    private String cacheName;
+
+    private String [] unionKey;
+
+    public String getCacheName() {
+        return cacheName;
+    }
+
+    public void setCacheName(String cacheName) {
+        this.cacheName = cacheName;
+    }
+
+    public String[] getUnionKey() {
+        return unionKey;
+    }
+
+    public void setUnionKey(String[] unionKey) {
+        this.unionKey = unionKey;
+    }
+}

+ 19 - 0
elab-redis/src/main/java/com/elab/redis/redisson/RedissonDecorator.java

@@ -0,0 +1,19 @@
+package com.elab.redis.redisson;
+
+import org.redisson.Redisson;
+import org.redisson.config.Config;
+
+/**
+ * Redisson 装饰器,方便后续如果要植入某些改动可以直接重写方法
+ *
+ * @author : liukx
+ * @time : 2020/7/8 - 16:46
+ */
+public class RedissonDecorator extends Redisson {
+
+
+    public RedissonDecorator(Config config) {
+        super(config);
+    }
+
+}

+ 29 - 0
elab-redis/src/main/java/com/elab/redis/spring/data/RedisTemplateDecorator.java

@@ -0,0 +1,29 @@
+package com.elab.redis.spring.data;
+
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+
+/**
+ * redisTemplate 装饰器
+ *
+ * @author : liukx
+ * @time : 2020/7/8 - 16:50
+ */
+public class RedisTemplateDecorator<String, CacheValueModel> extends RedisTemplate<String, CacheValueModel> {
+
+    @Override
+    public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
+        try {
+            return super.execute(action, exposeConnection, pipeline);
+        } catch (Exception e) {
+            logger.error("缓存执行失败", e);
+        }
+        return null;
+    }
+
+    @Override
+    protected <T> T postProcessResult(T result, RedisConnection conn, boolean existingConnection) {
+        return super.postProcessResult(result, conn, existingConnection);
+    }
+}

+ 74 - 0
elab-redis/src/main/java/com/elab/redis/utils/CacheParseUtil.java

@@ -0,0 +1,74 @@
+package com.elab.redis.utils;
+
+import com.elab.redis.annotation.CacheLoopSubmit;
+import com.elab.redis.model.annotation.LoopSubmitModel;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.core.annotation.AnnotationAttributes;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Set;
+
+/**
+ * 缓存解析类
+ *
+ * @author : liukx
+ * @time : 2020/7/9 - 19:42
+ */
+public class CacheParseUtil {
+
+    /**
+     * 是否包含特定注解集合
+     *
+     * @param annotations 注解列表
+     * @param element     对象元素
+     * @return
+     */
+    public static boolean isContainAnnotations(Set<Class<? extends Annotation>> annotations, AnnotatedElement element) {
+        boolean isContain = false;
+        for (Class<? extends Annotation> annotation : annotations) {
+            if (isContainAnnotation(element, annotation)) {
+                isContain = true;
+                break;
+            }
+        }
+        return isContain;
+    }
+
+    /**
+     * 是否包含特定的注解
+     *
+     * @param element
+     * @param annotation
+     * @return
+     */
+    public static boolean isContainAnnotation(AnnotatedElement element, Class<? extends Annotation> annotation) {
+        return AnnotatedElementUtils.hasAnnotation(element, annotation);
+    }
+
+
+    public static boolean isLoopSubmit(AnnotatedElement element) {
+        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
+                element, CacheLoopSubmit.class, false, false);
+        if (attributes != null) {
+            return true;
+        }
+        return false;
+    }
+
+    public static LoopSubmitModel parseLoopSubmit(AnnotatedElement element) {
+        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
+                element, CacheLoopSubmit.class, false, false);
+        if (attributes != null) {
+            String cacheName = attributes.getString("cacheName");
+            String[] unionKeys = attributes.getStringArray("unionKey");
+            LoopSubmitModel model = new LoopSubmitModel();
+            model.setCacheName(cacheName);
+            model.setUnionKey(unionKeys);
+            return model;
+        }
+
+        return null;
+    }
+
+}

+ 32 - 0
elab-redis/src/test/java/com/elab/redis/RedisSpringBoot.java

@@ -0,0 +1,32 @@
+package com.elab.redis;
+
+import com.elab.redis.config.CacheAutoConfiguration;
+import org.junit.runner.RunWith;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/8 - 15:14
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+        classes = {RedissonApplication.class, CacheAutoConfiguration.class}
+)
+@EnableCaching
+public class RedisSpringBoot {
+    @Autowired
+    protected RedissonClient client;
+
+
+    protected void run(int count, Runnable r) {
+        for (int i = 0; i < count; i++) {
+            new Thread(r).start();
+        }
+
+    }
+
+}

+ 15 - 0
elab-redis/src/test/java/com/elab/redis/RedissonApplication.java

@@ -0,0 +1,15 @@
+package com.elab.redis;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
+
+@SpringBootApplication
+@EnableCaching
+public class RedissonApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(RedissonApplication.class, args);
+    }
+    
+}

+ 60 - 0
elab-redis/src/test/java/com/elab/redis/cache/CacheTest.java

@@ -0,0 +1,60 @@
+package com.elab.redis.cache;
+
+import com.alibaba.fastjson.JSONObject;
+import com.elab.core.utils.RandomUtils;
+import com.elab.redis.RedisSpringBoot;
+import com.elab.redis.service.IDemoService;
+import com.elab.redis.service.impl.DemoServiceImpl;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Import;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 20:32
+ */
+@Import({DemoServiceImpl.class})
+public class CacheTest extends RedisSpringBoot {
+
+    private Logger logger = LoggerFactory.getLogger(CacheTest.class);
+
+    @Autowired
+    private IDemoService demoService;
+
+    @Test
+    public void testCache() throws Exception {
+        logger.info(" 开始进入竞争场景 ...");
+        run(10, () -> {
+            try {
+                demoService.submit("abc_" + RandomUtils.randomString(5));
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        });
+        System.in.read();
+    }
+
+    @Test
+    public void testNoCache() {
+        demoService.noCache("aaaa");
+    }
+
+    @Test
+    public void testSpringCache() throws Exception {
+        String cdx = demoService.springCache("ddd");
+        System.out.println(cdx);
+    }
+
+    @Test
+    public void testSpringCacheObject() throws Exception {
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("lkx", "abc");
+        jsonObject.put("xxx", "fff");
+        jsonObject.put("ccc", "ddd");
+        String cdx = demoService.springcacheObject(jsonObject);
+        System.out.println(cdx);
+    }
+
+}

+ 38 - 0
elab-redis/src/test/java/com/elab/redis/redisson/RedisAutoConfigurationTest.java

@@ -0,0 +1,38 @@
+package com.elab.redis.redisson;
+
+import com.elab.redis.RedissonApplication;
+import com.elab.redis.config.CacheAutoConfiguration;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.redisson.api.RAtomicLong;
+import org.redisson.api.RFuture;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+        classes = {RedissonApplication.class, CacheAutoConfiguration.class}
+)
+public class RedisAutoConfigurationTest {
+
+    @Autowired
+    private RedissonClient client;
+
+    /**
+     * 同步和异步
+     */
+    @Test
+    public void testApp() throws Exception {
+        RAtomicLong longObject = client.getAtomicLong("myLong");
+        // 同步执行方式
+        longObject.compareAndSet(3, 401);
+        // 异步执行方式
+        RFuture<Boolean> result = longObject.compareAndSetAsync(3, 401);
+        Boolean isLock = result.get();
+
+    }
+
+
+}

+ 45 - 0
elab-redis/src/test/java/com/elab/redis/redisson/RedissonAutoConfigurationTest.java

@@ -0,0 +1,45 @@
+package com.elab.redis.redisson;
+
+import com.elab.redis.RedissonApplication;
+import com.elab.redis.config.CacheAutoConfiguration;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.redisson.api.RAtomicDouble;
+import org.redisson.api.RMap;
+import org.redisson.api.RedissonClient;
+import org.redisson.client.codec.StringCodec;
+import org.redisson.codec.JsonJacksonCodec;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.redis.core.BoundHashOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+        classes = {RedissonApplication.class, CacheAutoConfiguration.class}
+)
+public class RedissonAutoConfigurationTest {
+
+    @Autowired
+    private RedissonClient redisson;
+
+    @Autowired
+    private RedisTemplate<String, String> template;
+
+    @Test
+    public void testApp() {
+        redisson.getKeys().flushall();
+        RAtomicDouble atomicDouble = redisson.getAtomicDouble("");
+        RMap<Object, Object> map = redisson.getMap("", JsonJacksonCodec.INSTANCE);
+        RMap<String, String> m = redisson.getMap("test", StringCodec.INSTANCE);
+        m.put("1", "2");
+        BoundHashOperations<String, String, String> hash = template.boundHashOps("test");
+        String t = hash.get("1");
+        assertThat(t).isEqualTo("2");
+    }
+
+
+}

+ 33 - 0
elab-redis/src/test/java/com/elab/redis/redisson/RedissonCacheManagerAutoConfigurationTest.java

@@ -0,0 +1,33 @@
+package com.elab.redis.redisson;
+
+import com.elab.redis.RedissonApplication;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+        classes = RedissonApplication.class,
+        properties = {
+//            "spring.redis.redisson.config=classpath:redisson.yaml",
+//            "spring.redis.timeout=10000",
+//            "spring.cache.type=redis",
+        })
+public class RedissonCacheManagerAutoConfigurationTest {
+
+    @Autowired
+    private CacheManager cacheManager;
+    
+    @Test
+    public void testApp() {
+        Cache cache = cacheManager.getCache("test");
+
+        Assert.assertNotNull(cache);
+    }
+    
+}

+ 13 - 0
elab-redis/src/test/java/com/elab/redis/redisson/RedissonRestApplication.java

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

+ 48 - 0
elab-redis/src/test/java/com/elab/redis/redisson/RedissonSessionManagerAutoConfigurationTest.java

@@ -0,0 +1,48 @@
+package com.elab.redis.redisson;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+        classes = RedissonRestApplication.class,
+        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
+        properties = {
+            "spring.redis.redisson.config=classpath:redisson.yaml",
+            "spring.session.store-type=redis",
+            "spring.session.timeout.seconds=900",
+        })
+public class RedissonSessionManagerAutoConfigurationTest {
+
+	@LocalServerPort
+	private int port;
+
+	@Autowired
+	private TestRestTemplate restTemplate;
+
+    @Test
+    public void testApp() {
+        List<String> cookies = this.restTemplate.getForEntity("http://localhost:" + port + "/api/set", String.class).getHeaders().get("Set-Cookie");
+        Assertions.assertThat(cookies).isNotEmpty();
+
+        HttpHeaders requestHeaders = new HttpHeaders();
+        requestHeaders.put(HttpHeaders.COOKIE, cookies);
+        HttpEntity<Void> request = new HttpEntity<>(requestHeaders);
+
+        ResponseEntity<String> response = restTemplate.exchange("http://localhost:" + port + "/api/get", HttpMethod.GET, request, String.class);
+        Assertions.assertThat(response.getBody()).isEqualTo("1");
+    }
+    
+}

+ 25 - 0
elab-redis/src/test/java/com/elab/redis/redisson/TestRestController.java

@@ -0,0 +1,25 @@
+package com.elab.redis.redisson;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpSession;
+
+/**
+ * @author Nikita Koksharov
+ */
+@RestController
+@RequestMapping("/api")
+public class TestRestController {
+
+    @GetMapping("/set")
+    public void setData(HttpSession httpSession) {
+        httpSession.setAttribute("testattr", "1");
+    }
+
+    @GetMapping("/get")
+    public String getData(HttpSession httpSession) {
+        return (String) httpSession.getAttribute("testattr");
+    }
+}

+ 56 - 0
elab-redis/src/test/java/com/elab/redis/redisson/doc/ConfigurationTest.java

@@ -0,0 +1,56 @@
+package com.elab.redis.redisson.doc;
+
+import com.elab.redis.RedissonApplication;
+import com.elab.redis.config.CacheAutoConfiguration;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.redisson.api.RAtomicLong;
+import org.redisson.api.RFuture;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * 配置方法测试
+ *
+ * @author : liukx
+ * @time : 2020/7/9 - 13:57
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+        classes = {RedissonApplication.class, CacheAutoConfiguration.class}
+)
+public class ConfigurationTest {
+    @Autowired
+    private RedissonClient client;
+
+
+    private void start() {
+        new Thread(() -> {
+
+        }).start();
+    }
+
+    /**
+     * 同步和异步
+     */
+    @Test
+    public void testApp() throws Exception {
+        RAtomicLong longObject = client.getAtomicLong("myLong");
+        // 同步执行方式
+        boolean b = longObject.compareAndSet(3, 401);
+        System.out.println(b);
+        // 异步执行方式
+        RFuture<Boolean> result = longObject.compareAndSetAsync(3, 401);
+        result.thenAccept(res -> {
+            // 处理返回
+            System.out.println("=============成功==========" + res);
+        }).exceptionally(exception -> {
+            // 处理错误
+            System.out.println("============异常===========");
+            return null;
+        });
+
+    }
+}

+ 74 - 0
elab-redis/src/test/java/com/elab/redis/redisson/doc/DistributedCollectionsTest.java

@@ -0,0 +1,74 @@
+package com.elab.redis.redisson.doc;
+
+import com.elab.redis.RedisSpringBoot;
+import com.elab.redis.redisson.doc.model.SomeObject;
+import org.junit.Test;
+import org.redisson.api.RFuture;
+import org.redisson.api.RMap;
+import org.redisson.api.RMapCache;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 分布式集合操作
+ *
+ * @author : liukx
+ * @time : 2020/7/9 - 16:37
+ */
+public class DistributedCollectionsTest extends RedisSpringBoot {
+
+
+    @Test
+    public void mapTest() {
+        RMap<String, SomeObject> map = client.getMap("anyMap");
+        SomeObject prevObject = map.put("123", new SomeObject());
+        SomeObject currentObject = map.putIfAbsent("323", new SomeObject());
+        SomeObject obj = map.remove("123");
+
+        map.fastPut("321", new SomeObject());
+        map.fastRemove("321");
+
+        RFuture<SomeObject> putAsyncFuture = map.putAsync("321", new SomeObject());
+        RFuture<Boolean> fastPutAsyncFuture = map.fastPutAsync("321", new SomeObject());
+
+        map.fastPutAsync("321", new SomeObject());
+        map.fastRemoveAsync("321");
+    }
+
+    @Test
+    public void lockMapTest() {
+//        RMap<MyKey, MyValue> map = client.getMap("anyMap");
+//        MyKey k = new MyKey();
+//        RLock keyLock = map.getLock(k);
+//        keyLock.lock();
+//        try {
+//            MyValue v = map.get(k);
+//            // 其他业务逻辑
+//        } finally {
+//            keyLock.unlock();
+//        }
+//
+//        RReadWriteLock rwLock = map.getReadWriteLock(k);
+//        rwLock.readLock().lock();
+//        try {
+//            MyValue v = map.get(k);
+//            // 其他业务逻辑
+//        } finally {
+//            keyLock.readLock().unlock();
+//        }
+    }
+
+    @Test
+    public void evictionMapTest() {
+        RMapCache<String, SomeObject> map = client.getMapCache("anyMap");
+        // 有效时间 ttl = 10分钟
+        map.put("key1", new SomeObject("1"), 10, TimeUnit.MINUTES);
+        // 有效时间 ttl = 10分钟, 最长闲置时间 maxIdleTime = 10秒钟
+        map.put("key1", new SomeObject("1"), 10, TimeUnit.MINUTES, 10, TimeUnit.SECONDS);
+
+        // 有效时间 = 3 秒钟
+        map.putIfAbsent("key2", new SomeObject("2"), 3, TimeUnit.SECONDS);
+        // 有效时间 ttl = 40秒钟, 最长闲置时间 maxIdleTime = 10秒钟
+        map.putIfAbsent("key2", new SomeObject("2"), 40, TimeUnit.SECONDS, 10, TimeUnit.SECONDS);
+    }
+}

+ 20 - 0
elab-redis/src/test/java/com/elab/redis/redisson/doc/DistributedLocksAndSynchronizersTest.java

@@ -0,0 +1,20 @@
+package com.elab.redis.redisson.doc;
+
+import com.elab.redis.RedisSpringBoot;
+import org.junit.Test;
+import org.redisson.api.RLock;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 17:00
+ */
+public class DistributedLocksAndSynchronizersTest extends RedisSpringBoot {
+
+    @Test
+    public void lockTest() {
+        RLock lock = client.getLock("anyLock");
+        lock.lock(10, TimeUnit.SECONDS);
+    }
+}

+ 145 - 0
elab-redis/src/test/java/com/elab/redis/redisson/doc/DistributedTest.java

@@ -0,0 +1,145 @@
+package com.elab.redis.redisson.doc;
+
+import com.elab.core.utils.RandomUtils;
+import com.elab.redis.RedisSpringBoot;
+import com.elab.redis.redisson.doc.model.AnyObject;
+import com.elab.redis.redisson.doc.model.BloomFilterObject;
+import com.elab.redis.redisson.doc.model.SomeObject;
+import org.junit.Test;
+import org.redisson.api.*;
+import org.redisson.api.listener.MessageListener;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * 分布式对象
+ *
+ * @author : liukx
+ * @time : 2020/7/9 - 14:10
+ */
+public class DistributedTest extends RedisSpringBoot {
+
+    private void log(int index, Object text) {
+        System.out.println("-----[" + index + "]----->" + text);
+    }
+
+    @Test
+    public void bigBucket() {
+        RBucket<AnyObject> bucket = client.getBucket("anyObject");
+
+        bucket.set(new AnyObject(1));
+        AnyObject obj = bucket.get();
+        log(1, obj);
+        boolean b = bucket.trySet(new AnyObject(3));
+        log(2, b);
+        boolean b1 = bucket.compareAndSet(new AnyObject(4), new AnyObject(5));
+        log(3, b1);
+        AnyObject andSet = bucket.getAndSet(new AnyObject(6));
+        log(4, andSet);
+    }
+
+    @Test
+    public void bitSetTest() {
+        RBitSet set = client.getBitSet("simpleBitset");
+        set.set(0, true);
+        set.set(1812, false);
+        set.clear(0);
+        set.xor("anotherBitset");
+    }
+
+    @Test
+    public void atomicTest() {
+        RAtomicLong atomicLong = client.getAtomicLong("myAtomicLong");
+        atomicLong.set(3);
+        atomicLong.incrementAndGet();
+        atomicLong.get();
+    }
+
+    @Test
+    public void pull() throws Exception {
+        // 支持正则表达式
+//        RTopic topic = client.getTopic("anyTopic*");
+        RTopic topic = client.getTopic("anyTopic");
+        topic.addListener(new MessageListener<SomeObject>() {
+            @Override
+            public void onMessage(CharSequence charSequence, SomeObject someObject) {
+                log(2, someObject.toString());
+            }
+        });
+        log(1, "开始准备就绪");
+        System.in.read();
+    }
+
+    @Test
+    public void push() throws Exception {
+        for (int i = 0; i < 5; i++) {
+
+
+            run(10, () -> {
+                // in other thread or JVM
+                RTopic pushTopic = client.getTopic("anyTopic");
+                long clientsReceivedMessage = pushTopic.publish(new SomeObject("abc_" + RandomUtils.randomString(5)));
+                log(1, clientsReceivedMessage);
+            });
+            Thread.sleep(5000);
+            log(i, "准备发送");
+        }
+    }
+
+
+    @Test
+    public void bloomFilterTest() {
+        RBloomFilter<BloomFilterObject> bloomFilter = client.getBloomFilter("sample");
+        // initialize bloom filter with
+        // expectedInsertions = 55000000
+        // falseProbability = 0.03
+        // 使用布隆过滤器之前必须先设置好容量大小和区分度
+        bloomFilter.tryInit(55000000L, 0.03);
+
+        bloomFilter.add(new BloomFilterObject("field1Value", "field2Value"));
+        bloomFilter.add(new BloomFilterObject("field5Value", "field8Value"));
+
+        boolean contains = bloomFilter.contains(new BloomFilterObject("field1Value", "field2Value"));
+        log(1, contains);
+        long count = bloomFilter.count();
+        log(2, count);
+    }
+
+    @Test
+    public void hyperLogTest() {
+        // 集群模式下 需要带前缀,表示落在同一个节点上
+        String prefix = "{xxx}_";
+        RHyperLogLog<Integer> log = client.getHyperLogLog(prefix + "log");
+        log.add(1);
+        log.add(2);
+        log.add(4);
+
+        // 添加进去判断是否存在
+        log(1, log.add(3));
+
+        // 判断去重之后得到的总数,如果是不同节点可能会统计报错
+        log(2, log.countWith(prefix + "log1"));
+
+        log(5, log.count());
+    }
+
+
+    @Test
+    public void rateLimiterTest() {
+        RRateLimiter rateLimiter = client.getRateLimiter("myRateLimiter");
+// 初始化
+// 最大流速 = 每1秒钟产生10个令牌
+        rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
+
+        CountDownLatch latch = new CountDownLatch(2);
+        rateLimiter.acquire(3);
+// ...
+
+        Thread t = new Thread(() -> {
+            rateLimiter.acquire(2);
+            // ...
+        });
+    }
+
+
+}

+ 31 - 0
elab-redis/src/test/java/com/elab/redis/redisson/doc/model/AnyObject.java

@@ -0,0 +1,31 @@
+package com.elab.redis.redisson.doc.model;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 14:14
+ */
+public class AnyObject {
+    private int index;
+
+    public AnyObject() {
+    }
+
+    public AnyObject(int index) {
+        this.index = index;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    @Override
+    public String toString() {
+        return "AnyObject{" +
+                "index=" + index +
+                '}';
+    }
+}

+ 33 - 0
elab-redis/src/test/java/com/elab/redis/redisson/doc/model/BloomFilterObject.java

@@ -0,0 +1,33 @@
+package com.elab.redis.redisson.doc.model;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 15:16
+ */
+public class BloomFilterObject {
+
+    private String field1Value;
+
+    private String field2Value;
+
+    public BloomFilterObject(String field1Value, String field2Value) {
+        this.field1Value = field1Value;
+        this.field2Value = field2Value;
+    }
+
+    public String getField1Value() {
+        return field1Value;
+    }
+
+    public void setField1Value(String field1Value) {
+        this.field1Value = field1Value;
+    }
+
+    public String getField2Value() {
+        return field2Value;
+    }
+
+    public void setField2Value(String field2Value) {
+        this.field2Value = field2Value;
+    }
+}

+ 33 - 0
elab-redis/src/test/java/com/elab/redis/redisson/doc/model/SomeObject.java

@@ -0,0 +1,33 @@
+package com.elab.redis.redisson.doc.model;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 14:27
+ */
+public class SomeObject {
+
+
+    private String text;
+
+    public SomeObject() {
+    }
+
+    public SomeObject(String text) {
+        this.text = text;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    @Override
+    public String toString() {
+        return "SomeObject{" +
+                "text='" + text + '\'' +
+                '}';
+    }
+}

+ 20 - 0
elab-redis/src/test/java/com/elab/redis/service/IDemoService.java

@@ -0,0 +1,20 @@
+package com.elab.redis.service;
+
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 20:30
+ */
+public interface IDemoService {
+
+
+    public String submit(String text) throws Exception;
+
+    public String springCache(String text) throws Exception;
+
+    public String springcacheObject(JSONObject jsonObject) throws Exception;
+
+    public String noCache(String text);
+
+}

+ 51 - 0
elab-redis/src/test/java/com/elab/redis/service/impl/DemoServiceImpl.java

@@ -0,0 +1,51 @@
+package com.elab.redis.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.elab.core.utils.RandomUtils;
+import com.elab.redis.annotation.CacheLoopSubmit;
+import com.elab.redis.service.IDemoService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author : liukx
+ * @time : 2020/7/9 - 20:31
+ */
+@Component
+public class DemoServiceImpl implements IDemoService {
+
+    private volatile int count = 100;
+
+    private Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
+
+    @Override
+    @CacheLoopSubmit(unionKey = "[0]", cacheName = "demo", timeOut = 10)
+    public String submit(String text) throws Exception {
+
+        count--;
+        Thread.sleep(500);
+        logger.info(text + " 抢到一个资源 , 资源池  " + count);
+
+        return "lkx";
+    }
+
+    @Cacheable(cacheNames = "redisson")
+    public String springCache(String text) {
+        String s = "xiong_" + RandomUtils.randomString(2);
+        System.out.println("业务层获取参数值:" + text + " 返回值 : " + s);
+        return s;
+    }
+
+    @Cacheable(cacheNames = "springcacheObject")
+    public String springcacheObject(JSONObject jsonObject) {
+        System.out.println("业务层获取参数值:" + jsonObject.toJSONString());
+        return jsonObject.toJSONString();
+    }
+
+    @Override
+    public String noCache(String text) {
+        return null;
+    }
+}

+ 25 - 0
elab-redis/src/test/java/com/elab/redis/spring/SpringDataTest.java

@@ -0,0 +1,25 @@
+package com.elab.redis.spring;
+
+import com.elab.redis.CacheTemplate;
+import com.elab.redis.RedisSpringBoot;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * springData
+ *
+ * @author : liukx
+ * @time : 2020/7/15 - 11:12
+ */
+public class SpringDataTest extends RedisSpringBoot {
+
+    @Autowired
+    private CacheTemplate cacheTemplate;
+
+    @Test
+    public void set() {
+        cacheTemplate.string().set("lkx", "123", 10, TimeUnit.SECONDS);
+    }
+}

+ 48 - 0
elab-redis/src/test/java/com/elab/redis/utils/TestUtils.java

@@ -0,0 +1,48 @@
+package com.elab.redis.utils;
+
+import org.apache.commons.beanutils.BeanUtils;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 普通非容器测试
+ *
+ * @author : liukx
+ * @time : 2020/7/10 - 15:02
+ */
+public class TestUtils {
+
+    @Test
+    public void testHashCode() {
+        String json = " \t转换后的sql -\n" +
+                "select\n" +
+                "COUNT(DISTINCT created) as days\n" +
+                "from\n" +
+                "t_zhidi_activity_info\n" +
+                "where brand_id = ? and house_id = ? and activity_id = ?\n" +
+                "and activity_type\n" +
+                "= ? and recommend_customer_id = ? and `status` = 1\n" +
+                "and created >= ? and created <= ?1";
+        String a = "Aa";
+        String b = "BB";
+        int aa = a.hashCode();
+        int bb = b.hashCode();
+
+        System.out.println(aa + "\t" + bb);
+        System.out.println(json.hashCode());
+    }
+
+    @Test
+    public void testPropertity() throws Exception {
+        Map<String, String> map = new HashMap<>();
+        map.put("a", "b");
+
+        String data = "c";
+
+        String abc = BeanUtils.getProperty(data, "abc");
+        System.out.println(abc);
+
+    }
+}

+ 10 - 0
elab-redis/src/test/resources/application.yml

@@ -0,0 +1,10 @@
+
+spring:
+  redis:
+    database: 0
+    host: r-uf6e60u5cmlj0sx563pd.redis.rds.aliyuncs.com
+    password: xcGm4kTks6
+    port: 6379
+    timeout: 2s
+  application:
+    name: test-redis

+ 2 - 2
elab-rocketMQ/pom.xml

@@ -5,14 +5,14 @@
     <parent>
         <artifactId>elab-parent</artifactId>
         <groupId>com.elab.core</groupId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>elab-rocketMQ</artifactId>
 
     <properties>
-        <springframework.version>5.1.8.RELEASE</springframework.version>
+        <!--<springframework.version>5.1.8.RELEASE</springframework.version>-->
         <rocketmq-spring-boot-starter-version>2.0.3</rocketmq-spring-boot-starter-version>
         <springboot.version></springboot.version>
     </properties>

+ 162 - 0
elab-spring/README.md

@@ -0,0 +1,162 @@
+# Elab-Spring
+
+该项目用来针对一些Spring的拓展做一些统一的封装使得在使用上非常方便
+
+这里只是大概介绍一下功能的使用。
+
+直接使用的话
+
+```java
+// 如果有相应的配置文件参数,需要将配置文件注入到里面来
+@PropertySource(value = {"classpath:database.properties","classpath:autoConfig.properties"},encoding = "UTF-8")
+// 直接导入相关的配置类
+@Import({SpringCommonConfig.class, DataSourceConfigBean.class, JdbcBeanConfig.class, TransactionConfigBean.class})
+
+```
+
+
+## 功能介绍
+#### SpringCommonConfig:基本的Spring相关的配置
+
+封装了ClientHttpRequestFactory、RestTemplate、PropertyPlaceholderConfigurer等类.
+
+有一些可变的参数可以再配置文件中指定,这里面的数据都是默认值,可以不填写
+```perl
+# httpClient连接数
+httpClient.connect.timeOut=120000
+# httpClient超时时间
+httpClient.read.timeOut=120000
+# 读取property配置文件的路径
+spring.resources.path=classpath:*.properties
+```
+
+
+#### DataSourceConfigBean : 数据源配置
+
+```perl
+
+default.driverClassName=
+default.url=
+default.username=
+default.password=
+default.filters=
+default.initialSize=5
+default.minIdle=10
+default.maxActive=50
+```
+
+
+#### JdbcBeanConfig : JDBC封装类配置
+可选配置
+```perl
+# jdbc的sql文件目录
+jdbc.config.path=sql
+
+# dao接口层扫描
+jdbc.config.scan=com.elab.**
+
+```
+
+#### TransactionConfigBean : 事物配置
+针对事物相关的类进行配置
+
+> 这里需要注意的是一般 TransactionConfigBean、JdbcBeanConfig 依赖 DataSourceConfigBean 所以使用的时候,需要将这三个类都一次性导入
+
+
+#### SpringMvcConfig : 基本的SpringMVC容器相关的配置
+
+封装了MultipartResolver等相关配置
+
+```perl
+# 文件上传大小配置
+mvc.multipartResolver.MaxUploadSize=10485760
+```
+
+#### CommonException : 全局异常定义
+直接注入就拥有全局异常功能
+
+
+
+#### LogResponseBodyAdvice : 日志id作为结果集返回
+直接注入拥有全局返回日志id的功能
+
+
+#### SwaggerConfigBean : api对象配置
+
+```
+#是否开启swagger
+swagger.enable=必填
+# controller路径
+swagger2.basePackage=必填
+# api的标题
+swagger2.title=必填
+# 描述
+swagger2.description=必填
+# 团队服务的URL
+swagger2.termsOfServiceUrl=必填
+# 许可证地址
+swagger2.licenseUrl=必填
+# 版本号
+swagger2.version=必填
+```
+
+
+#### RestTemplateUtils
+- 新增IRestFallback回调接口
+    - DefaultRestFallBack 默认的回调 : 一旦请求出现异常,则将该次请求数据记录到`mng_http_failure_data`表中
+> 如果业务有特殊处理,可以实现该接口去覆盖DefaultRestFallBack类的实现。
+
+```sql
+CREATE TABLE `mng_http_failure_data` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `url` varchar(200) DEFAULT NULL COMMENT '请求路径',
+  `req_method` varchar(10) DEFAULT NULL COMMENT '请求方式 GET/POST',
+  `req_body` text COMMENT '请求参数',
+  `res_body` text COMMENT '返回结果',
+  `header_body` text COMMENT '请求头信息',
+  `req_retry` int(11) DEFAULT NULL COMMENT '请求重试次数',
+  `req_status` int(11) DEFAULT NULL COMMENT '请求状态 -1失败 1成功 ',
+  `cat_id` varchar(50) DEFAULT NULL COMMENT '链路编号',
+  `error_msg` varchar(500) DEFAULT NULL,
+  `status` int(11) DEFAULT NULL COMMENT '状态:1  有效  -1  无效',
+  `created` datetime DEFAULT NULL COMMENT '创建时间',
+  `updated` datetime DEFAULT NULL COMMENT '修改时间',
+  `creator` varchar(20) DEFAULT NULL COMMENT '创建者',
+  `updator` varchar(20) DEFAULT NULL COMMENT '修改者',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='http故障数据记录';
+```
+**如何应用到业务中**
+1. 配置类
+```java
+@Bean
+public IRestFallback restFallback() {
+	DefaultRestFallBack restFallBack = new DefaultRestFallBack();
+   return restFallBack;
+}
+
+@Bean
+public RestTemplateUtils restTemplateUtils(@Autowired RestTemplate restTemplate) {
+	RestTemplateUtils restTemplateUtils = new RestTemplateUtils();
+	restTemplateUtils.setRestTemplate(restTemplate);
+	// 将默认的配置类加入到其中
+	restTemplateUtils.setRestFallback(restFallback());
+	return restTemplateUtils;
+}
+```
+
+具体使用:
+
+```java
+
+// 默认采用的失败回调 -> DefaultRestFallBack
+String post = restTemplateUtils.post(url, jsonObject,
+  String.class);
+
+// 自定义采用的失败回调 --> 实现IRestFallback接口 = MyRestFallBack
+String post = restTemplateUtils.post(url, jsonObject,
+  String.class, new MyRestFallBack());
+
+```
+
+

+ 13 - 1
elab-spring/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>elab-parent</artifactId>
         <groupId>com.elab.core</groupId>
-        <version>2.0.4.13-SNAPSHOT</version>
+        <version>2.0.4.14-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -42,6 +42,12 @@
             <artifactId>elab-log</artifactId>
             <version>${project.version}</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.elab.core</groupId>
+            <artifactId>elab-db</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <!-- Spring Core -->
         <dependency>
             <groupId>org.springframework</groupId>
@@ -64,6 +70,7 @@
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-jdbc</artifactId>
+            <scope>provided</scope>
         </dependency>
 
         <!-- tx -->
@@ -142,6 +149,11 @@
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
 
         <!--<dependency>-->
         <!--<groupId>ch.qos.logback</groupId>-->

+ 49 - 0
elab-spring/src/main/java/com/elab/spring/callback/IRestFallback.java

@@ -0,0 +1,49 @@
+package com.elab.spring.callback;
+
+import org.springframework.http.HttpHeaders;
+
+/**
+ * http请求失败回调
+ *
+ * @author : liukx
+ * @time : 2020/6/17 - 13:57
+ */
+public interface IRestFallback<T> {
+    /**
+     * post异常执行请求
+     *
+     * @param url         请求URL
+     * @param httpHeaders 请求头
+     * @param reqParam    请求参数
+     * @param clazz       转换接口
+     * @param e           异常类型
+     * @return
+     */
+    T post(String url, HttpHeaders httpHeaders, Object reqParam, Class clazz, Exception e);
+
+    /**
+     * GET 异常执行请求
+     *
+     * @param url
+     * @param clazz
+     * @param e
+     * @return
+     */
+    T get(String url, Class clazz, Exception e);
+
+    /**
+     * 请求之前触发
+     *
+     * @param url
+     * @throws Exception
+     */
+    void requestBefore(String url, HttpHeaders httpHeaders, Object reqParam, Class clazz) throws Exception;
+
+    /**
+     * 请求之后触发
+     *
+     * @param url
+     * @throws Exception
+     */
+    void requestAfter(String url, HttpHeaders httpHeaders, Object reqParam, Class clazz) throws Exception;
+}

+ 329 - 0
elab-spring/src/main/java/com/elab/spring/callback/impl/DefaultRestFallBack.java

@@ -0,0 +1,329 @@
+package com.elab.spring.callback.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.dianping.cat.Cat;
+import com.elab.spring.callback.IRestFallback;
+import com.elab.spring.dao.MngHttpFailureDataDao;
+import com.elab.spring.dao.MngHttpInfoDao;
+import com.elab.spring.dao.entity.MngHttpFailureDataEntity;
+import com.elab.spring.dao.entity.MngHttpInfoEntity;
+import com.elab.spring.exception.rest.CoolingException;
+import com.elab.spring.model.rest.HttpUrlMetric;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.http.HttpHeaders;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.LongAdder;
+
+/**
+ * 默认的失败回调
+ *
+ * @author : liukx
+ * @time : 2020/6/17 - 14:12
+ */
+public class DefaultRestFallBack implements IRestFallback, ApplicationContextAware, InitializingBean {
+
+
+    @Value("${spring.application.name}")
+    private String applicationName;
+
+    private Logger logger = LoggerFactory.getLogger(DefaultRestFallBack.class);
+
+    private ApplicationContext applicationContext;
+
+    private MngHttpFailureDataDao httpFailureDataDao;
+
+    private MngHttpInfoDao httpInfoDao;
+    /**
+     * 各部分组件是否加载完毕
+     */
+    private boolean isReload = false;
+    /**
+     * 冷却功能是否开启
+     */
+    private boolean isEnableCoolingTime = false;
+    /**
+     * 是否记录日志表
+     */
+    private boolean isInsertLog = true;
+
+    private Map<String, HttpUrlMetric> httpUrlMetricMap = new ConcurrentHashMap<>();
+
+    private Integer failStatus = -1;
+
+    private Integer okStatus = 1;
+
+    private static Object lock = new Object();
+
+    private List<String> errorMsgList = Arrays.asList("Connection refused", "404 Not Found", "502 Bad Gateway");
+
+    public void setEnableCoolingTime(boolean enableCoolingTime) {
+        isEnableCoolingTime = enableCoolingTime;
+    }
+
+    public void setInsertLog(boolean insertLog) {
+        isInsertLog = insertLog;
+    }
+
+    /**
+     * 新增异常消息匹配
+     *
+     * @param msg
+     */
+    public void addErrorMsg(String msg) {
+        errorMsgList.add(msg);
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        if (this.applicationContext != null) {
+            this.httpFailureDataDao = this.applicationContext.getBean(MngHttpFailureDataDao.class);
+            this.httpInfoDao = this.applicationContext.getBean(MngHttpInfoDao.class);
+            if (this.httpFailureDataDao != null && this.httpInfoDao != null) {
+                logger.debug("加载完成http故障持久层对象");
+                isReload = true;
+            }
+        }
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        this.applicationContext = applicationContext;
+    }
+
+    @Override
+    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
+    public Object post(String url, HttpHeaders httpHeaders, Object reqParam, Class clazz, Exception e) {
+        if (this.isReload) {
+            try {
+                CoolingProcess(url, e);
+                invokeInsert(url, "POST", httpHeaders, reqParam, e);
+            } catch (Exception e1) {
+                logger.error("补偿失败", e1);
+                e1.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 冷却逻辑计算
+     *
+     * @param url
+     * @param e
+     * @throws Exception
+     */
+    private void CoolingProcess(String url, Exception e) throws Exception {
+        if (isEnableCoolingTime) {
+            HttpUrlMetric httpInfo = getHttpInfo(url);
+            logger.debug("异常信息:" + e.getMessage());
+            // 连接异常直接进入冷却
+            if (errorMsgList.contains(e.getMessage())) {
+                httpInfo.setReqStatus(failStatus);
+                Integer recoveryRetryCount = httpInfo.getRecoveryRetryCount() == null ? 1 : httpInfo
+                        .getRecoveryRetryCount() + 1;
+                long nextTime = System.currentTimeMillis() + (recoveryRetryCount * 60000);
+                Date nextRetryTime = new Date(nextTime);
+                updateHttpInfoReqStatus(url, httpInfo.getId(), failStatus, recoveryRetryCount, nextRetryTime);
+            } else if (e instanceof CoolingException) {
+                logger.debug("冷却异常不做计算");
+            } else {
+                triggerErrorCount(httpInfo);
+            }
+        }
+    }
+
+    /**
+     * 触发异常总数计算
+     *
+     * @param httpInfo
+     * @throws Exception
+     */
+    private void triggerErrorCount(HttpUrlMetric httpInfo) throws Exception {
+        httpInfo.getFailCount().increment();
+        triggerCount(httpInfo, httpInfo.getFailCount(), failStatus);
+    }
+
+    /**
+     * 触发调用成功计算
+     *
+     * @param httpInfo
+     * @throws Exception
+     */
+    private void triggerOKCount(HttpUrlMetric httpInfo) throws Exception {
+        LongAdder failCount = httpInfo.getFailCount();
+        if (failCount.intValue() > 0) {
+            // 自增
+            httpInfo.getOkCount().increment();
+        }
+        triggerCount(httpInfo, httpInfo.getOkCount(), okStatus);
+    }
+
+    /**
+     * 核心计算总数
+     *
+     * @param httpInfo
+     * @param count
+     * @param status
+     * @throws Exception
+     */
+    private void triggerCount(HttpUrlMetric httpInfo, LongAdder count, Integer status) throws Exception {
+        int criticalValue = httpInfo.getSimpleCount() / 2;
+        // 如果达到临界值
+        if (count.intValue() >= criticalValue) {
+            Integer id = httpInfo.getId();
+            Date nextRetryTime = null;
+            Integer recoveryRetryCount = httpInfo.getRecoveryRetryCount() + 1;
+            if (failStatus.equals(status)) {
+                long longTime = System.currentTimeMillis() + recoveryRetryCount * 60000;
+                nextRetryTime = new Date(longTime);
+            }
+            updateHttpInfoReqStatus(httpInfo.getUrl(), id, status, recoveryRetryCount, nextRetryTime);
+            // 重新计算
+            httpInfo = null;
+        }
+    }
+
+    private void invokeInsert(String url, String method, HttpHeaders httpHeaders, Object reqParam, Exception e) {
+        if (isInsertLog) {
+            try {
+                MngHttpFailureDataEntity httpFailureDataEntity = new MngHttpFailureDataEntity();
+                httpFailureDataEntity.setCatId(Cat.getCurrentMessageId());
+                httpFailureDataEntity.setCreated(new Date());
+                httpFailureDataEntity.setCreator(getClass().getSimpleName());
+                httpFailureDataEntity.setUrl(url);
+                httpFailureDataEntity.setReqStatus(-1);
+                httpFailureDataEntity.setReqBody(JSON.toJSONString(reqParam));
+                httpFailureDataEntity.setReqMethod(method);
+                httpFailureDataEntity.setHeaderBody(JSON.toJSONString(httpHeaders));
+                httpFailureDataEntity.setStatus(okStatus);
+                httpFailureDataEntity.setErrorMsg(e.getMessage());
+                httpFailureDataEntity.setReqRetry(0);
+                httpFailureDataEntity.setProjectName(applicationName);
+                int result = this.httpFailureDataDao.insert(httpFailureDataEntity);
+                logger.debug("记录失败日志成功 日志编号:" + result);
+            } catch (Exception e1) {
+                logger.error("故障存储数据异常", e1);
+                e1.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
+    public Object get(String url, Class clazz, Exception e) {
+        if (this.httpFailureDataDao != null) {
+            try {
+                CoolingProcess(url, e);
+                invokeInsert(url, "GET", null, null, e);
+            } catch (Exception e1) {
+                logger.error("补偿失败", e1);
+                e1.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+
+    @Override
+    //@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
+    public void requestBefore(String url, HttpHeaders httpHeaders, Object reqParam, Class clazz) throws Exception {
+        if (isReload && isEnableCoolingTime) {
+            // 获取http的配置信息
+            MngHttpInfoEntity httpInfo = getHttpInfo(url);
+            Date recoveryTime = httpInfo.getRecoveryTime();
+            long currentTime = System.currentTimeMillis();
+            if (failStatus.equals(httpInfo.getReqStatus())) {
+                if ((recoveryTime != null) && (recoveryTime.getTime() > currentTime)) {
+                    // 处于冷却时间
+                    throw new CoolingException(url + "处于冷却中");
+                }
+            }
+        }
+    }
+
+    /**
+     * 获取http详情的信息
+     * 这里希望访问正常的请求走本地缓存,如果是非正常的全部通过数据库读取
+     *
+     * @param url
+     * @return
+     * @throws Exception
+     */
+    private HttpUrlMetric getHttpInfo(String url) throws Exception {
+
+        HttpUrlMetric httpUrlMetric = httpUrlMetricMap.get(url);
+        // 只有在有效的时候才返回
+        if (httpUrlMetric != null) {
+            if (okStatus.equals(httpUrlMetric.getReqStatus())) {
+                return httpUrlMetric;
+            } else {
+                logger.debug("非正常状态,需要重新从数据库获取");
+            }
+        }
+        synchronized (lock) {
+            MngHttpInfoEntity searchEntity = new HttpUrlMetric();
+            searchEntity.setProjectName(this.applicationName);
+            searchEntity.setUrl(url);
+            List<MngHttpInfoEntity> httpInfoEntities = this.httpInfoDao.selectByList(searchEntity);
+
+            if (httpInfoEntities.size() > 0) {
+                MngHttpInfoEntity httpInfoEntity = httpInfoEntities.get(httpInfoEntities.size() - 1);
+                httpUrlMetric = new HttpUrlMetric();
+                BeanUtils.copyProperties(httpInfoEntity, httpUrlMetric);
+                // 只有在有效的时候进行缓存
+                if (okStatus.equals(httpInfoEntity.getReqStatus())) {
+                    httpUrlMetricMap.put(url, httpUrlMetric);
+                }
+            } else {
+                searchEntity.setCatId(Cat.getCurrentMessageId());
+                searchEntity.setReqStatus(okStatus);
+                searchEntity.setSimpleCount(20);
+                searchEntity.setRecoveryRetryCount(0);
+                searchEntity.setStatus(1);
+                searchEntity.setCreated(new Date());
+                int result = this.httpInfoDao.insert(searchEntity);
+                searchEntity.setId(result);
+                logger.debug("初始化MngHttpInfoEntity数据 : " + result);
+                httpUrlMetric = new HttpUrlMetric();
+                BeanUtils.copyProperties(searchEntity, httpInfoEntities);
+                httpUrlMetricMap.put(url, httpUrlMetric);
+            }
+            return httpUrlMetric;
+        }
+    }
+
+
+    @Override
+    //@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
+    public void requestAfter(String url, HttpHeaders httpHeaders, Object reqParam, Class clazz) throws Exception {
+        if (isEnableCoolingTime) {
+            HttpUrlMetric httpInfo = getHttpInfo(url);
+            triggerOKCount(httpInfo);
+        }
+    }
+
+    private void updateHttpInfoReqStatus(String url, Integer id, Integer status, Integer recoveryRetryCount, Date recoveryTime) throws Exception {
+        MngHttpInfoEntity updateInfo = new HttpUrlMetric();
+        updateInfo.setId(id);
+        updateInfo.setUpdated(new Date());
+        updateInfo.setReqStatus(status);
+        updateInfo.setRecoveryRetryCount(recoveryRetryCount);
+        updateInfo.setRecoveryTime(recoveryTime);
+        httpInfoDao.updateById(updateInfo);
+        logger.debug("URL : " + url + " 恢复到[" + status + "]状态");
+    }
+}

+ 32 - 0
elab-spring/src/main/java/com/elab/spring/callback/impl/RestFallbackAdaptor.java

@@ -0,0 +1,32 @@
+package com.elab.spring.callback.impl;
+
+import com.elab.spring.callback.IRestFallback;
+import org.springframework.http.HttpHeaders;
+
+/**
+ * 失败回调的适配器
+ *
+ * @author : liukx
+ * @time : 2020/7/6 - 13:59
+ */
+public class RestFallbackAdaptor<T> implements IRestFallback<T> {
+    @Override
+    public T post(String url, HttpHeaders httpHeaders, Object reqParam, Class clazz, Exception e) {
+        return null;
+    }
+
+    @Override
+    public T get(String url, Class clazz, Exception e) {
+        return null;
+    }
+
+    @Override
+    public void requestBefore(String url, HttpHeaders httpHeaders, Object reqParam, Class clazz) throws Exception {
+
+    }
+
+    @Override
+    public void requestAfter(String url, HttpHeaders httpHeaders, Object reqParam, Class clazz) throws Exception {
+
+    }
+}

+ 19 - 0
elab-spring/src/main/java/com/elab/spring/dao/MngHttpFailureDataDao.java

@@ -0,0 +1,19 @@
+package com.elab.spring.dao;
+
+
+import com.elab.core.aop.annotations.XmlGroupName;
+import com.elab.core.dao.IBaseDaoSupport;
+import com.elab.spring.dao.entity.MngHttpFailureDataEntity;
+
+
+/**
+ * http故障操作实体
+ *
+ * @author liukx
+ * @Date 2020-06-17 14:41
+ */
+@XmlGroupName("MngHttpFailureData")
+public interface MngHttpFailureDataDao extends IBaseDaoSupport<MngHttpFailureDataEntity> {
+    // 复杂业务自己写
+
+}

+ 17 - 0
elab-spring/src/main/java/com/elab/spring/dao/MngHttpInfoDao.java

@@ -0,0 +1,17 @@
+package com.elab.spring.dao;
+
+
+import com.elab.core.aop.annotations.XmlGroupName;
+import com.elab.core.dao.IBaseDaoSupport;
+import com.elab.spring.dao.entity.MngHttpInfoEntity;
+
+/**
+ * MngHttpInfoDao
+ *
+ * @author liukx
+ * @Date 2020-06-17 17:26
+ */
+@XmlGroupName("MngHttpInfo")
+public interface MngHttpInfoDao extends IBaseDaoSupport<MngHttpInfoEntity> {
+    // 复杂业务自己写
+}

+ 337 - 0
elab-spring/src/main/java/com/elab/spring/dao/entity/MngHttpFailureDataEntity.java

@@ -0,0 +1,337 @@
+package com.elab.spring.dao.entity;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.persistence.Column;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.util.Date;
+
+/**
+ * http故障数据记录
+ *
+ * @author liukx
+ * @Date 2020-06-17 14:41
+ */
+@Table(name = "mng_http_failure_data")
+@ApiModel(description = "http故障数据记录 实体")
+public class MngHttpFailureDataEntity {
+
+    /**
+     * Id
+     */
+    @Id
+    @ApiModelProperty(name = "id", value = "主键")
+    private Integer id;
+
+    /**
+     * 请求路径
+     */
+    @Column(name = "url")
+    @ApiModelProperty(name = "url", value = "请求路径")
+    private String url;
+
+    @Column(name = "project_name")
+    @ApiModelProperty(name = "projectName", value = "项目名称")
+    private String projectName;
+
+    /**
+     * 请求方式 GET/POST
+     */
+    @Column(name = "req_method")
+    @ApiModelProperty(name = "reqMethod", value = "请求方式 GET/POST")
+    private String reqMethod;
+
+    /**
+     * 请求参数
+     */
+    @Column(name = "req_body")
+    @ApiModelProperty(name = "reqBody", value = "请求参数")
+    private String reqBody;
+
+    /**
+     * 返回结果
+     */
+    @Column(name = "res_body")
+    @ApiModelProperty(name = "resBody", value = "返回结果")
+    private String resBody;
+
+    /**
+     * 请求头信息
+     */
+    @Column(name = "header_body")
+    @ApiModelProperty(name = "headerBody", value = "请求头信息")
+    private String headerBody;
+
+    /**
+     * 请求重试次数
+     */
+    @Column(name = "req_retry")
+    @ApiModelProperty(name = "reqRetry", value = "请求重试次数")
+    private Integer reqRetry;
+
+    /**
+     * 请求状态 -1失败 1成功
+     */
+    @Column(name = "req_status")
+    @ApiModelProperty(name = "reqStatus", value = "请求状态 -1失败 1成功 ")
+    private Integer reqStatus;
+
+    /**
+     * 链路编号
+     */
+    @Column(name = "cat_id")
+    @ApiModelProperty(name = "catId", value = "链路编号")
+    private String catId;
+
+    /**
+     * 状态:1  有效  -1  无效
+     */
+    @Column(name = "status")
+    @ApiModelProperty(name = "status", value = "状态:1  有效  -1  无效")
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    @Column(name = "created")
+    @ApiModelProperty(name = "created", value = "创建时间")
+    private Date created;
+
+    /**
+     * 修改时间
+     */
+    @Column(name = "updated")
+    @ApiModelProperty(name = "updated", value = "修改时间")
+    private Date updated;
+
+    /**
+     * 创建者
+     */
+    @Column(name = "creator")
+    @ApiModelProperty(name = "creator", value = "创建者")
+    private String creator;
+
+    /**
+     * 修改者
+     */
+    @Column(name = "updator")
+    @ApiModelProperty(name = "updator", value = "修改者")
+    private String updator;
+
+    @Column(name = "error_msg")
+    private String errorMsg;
+
+    public String getProjectName() {
+        return projectName;
+    }
+
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+
+    /**
+     * 获取: Id
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * 设置: Id
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取: 请求路径
+     */
+    public String getUrl() {
+        return url;
+    }
+
+    /**
+     * 设置: 请求路径
+     */
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    /**
+     * 获取: 请求方式 GET/POST
+     */
+    public String getReqMethod() {
+        return reqMethod;
+    }
+
+    /**
+     * 设置: 请求方式 GET/POST
+     */
+    public void setReqMethod(String reqMethod) {
+        this.reqMethod = reqMethod;
+    }
+
+    /**
+     * 获取: 请求参数
+     */
+    public String getReqBody() {
+        return reqBody;
+    }
+
+    /**
+     * 设置: 请求参数
+     */
+    public void setReqBody(String reqBody) {
+        this.reqBody = reqBody;
+    }
+
+    /**
+     * 获取: 返回结果
+     */
+    public String getResBody() {
+        return resBody;
+    }
+
+    /**
+     * 设置: 返回结果
+     */
+    public void setResBody(String resBody) {
+        this.resBody = resBody;
+    }
+
+    /**
+     * 获取: 请求头信息
+     */
+    public String getHeaderBody() {
+        return headerBody;
+    }
+
+    /**
+     * 设置: 请求头信息
+     */
+    public void setHeaderBody(String headerBody) {
+        this.headerBody = headerBody;
+    }
+
+    /**
+     * 获取: 请求重试次数
+     */
+    public Integer getReqRetry() {
+        return reqRetry;
+    }
+
+    /**
+     * 设置: 请求重试次数
+     */
+    public void setReqRetry(Integer reqRetry) {
+        this.reqRetry = reqRetry;
+    }
+
+    /**
+     * 获取: 请求状态 -1失败 1成功
+     */
+    public Integer getReqStatus() {
+        return reqStatus;
+    }
+
+    /**
+     * 设置: 请求状态 -1失败 1成功
+     */
+    public void setReqStatus(Integer reqStatus) {
+        this.reqStatus = reqStatus;
+    }
+
+    /**
+     * 获取: 链路编号
+     */
+    public String getCatId() {
+        return catId;
+    }
+
+    /**
+     * 设置: 链路编号
+     */
+    public void setCatId(String catId) {
+        this.catId = catId;
+    }
+
+    /**
+     * 获取: 状态:1  有效  -1  无效
+     */
+    public Integer getStatus() {
+        return status;
+    }
+
+    /**
+     * 设置: 状态:1  有效  -1  无效
+     */
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    /**
+     * 获取: 创建时间
+     */
+    public Date getCreated() {
+        return created;
+    }
+
+    /**
+     * 设置: 创建时间
+     */
+    public void setCreated(Date created) {
+        this.created = created;
+    }
+
+    /**
+     * 获取: 修改时间
+     */
+    public Date getUpdated() {
+        return updated;
+    }
+
+    /**
+     * 设置: 修改时间
+     */
+    public void setUpdated(Date updated) {
+        this.updated = updated;
+    }
+
+    /**
+     * 获取: 创建者
+     */
+    public String getCreator() {
+        return creator;
+    }
+
+    /**
+     * 设置: 创建者
+     */
+    public void setCreator(String creator) {
+        this.creator = creator;
+    }
+
+    /**
+     * 获取: 修改者
+     */
+    public String getUpdator() {
+        return updator;
+    }
+
+    /**
+     * 设置: 修改者
+     */
+    public void setUpdator(String updator) {
+        this.updator = updator;
+    }
+}

+ 308 - 0
elab-spring/src/main/java/com/elab/spring/dao/entity/MngHttpInfoEntity.java

@@ -0,0 +1,308 @@
+package com.elab.spring.dao.entity;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.persistence.Column;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.util.Date;
+
+/**
+ * http请求信息
+ *
+ * @author liukx
+ * @Date 2020-06-17 17:26
+ */
+@Table(name = "mng_http_info")
+@ApiModel(description = "http请求信息 实体")
+public class MngHttpInfoEntity {
+
+     /**
+     * Id
+     * 
+     */
+    @Id
+    @ApiModelProperty(name = "id", value = "主键")
+    private Integer id;
+
+     /**
+     * ProjectName
+     * 
+     */
+    @Column(name = "project_name")
+    @ApiModelProperty(name = "projectName", value = "ProjectName")
+    private String projectName;
+
+     /**
+     * Url
+     * 
+     */
+    @Column(name = "url")
+    @ApiModelProperty(name = "url", value = "Url")
+    private String url;
+
+     /**
+     * 请求状态 1 有效 0 超时 -1 服务连接不上
+     * 
+     */
+    @Column(name = "req_status")
+    @ApiModelProperty(name = "reqStatus", value = "请求状态 1 有效 0 超时 -1 服务连接不上")
+    private Integer reqStatus;
+
+     /**
+     * 样本计算总数
+     * 
+     */
+    @Column(name = "simple_count")
+    @ApiModelProperty(name = "simpleCount", value = "样本计算总数")
+    private Integer simpleCount;
+
+     /**
+     * 失败重试总数
+     * 
+     */
+    @Column(name = "recovery_retry_count")
+    @ApiModelProperty(name = "recoveryRetryCount", value = "失败重试总数")
+    private Integer recoveryRetryCount;
+
+     /**
+     * 恢复(冷却)时间
+     * 
+     */
+    @Column(name = "recovery_time")
+    @ApiModelProperty(name = "recoveryTime", value = "恢复(冷却)时间")
+    private Date recoveryTime;
+
+     /**
+     * CatId
+     * 
+     */
+    @Column(name = "cat_id")
+    @ApiModelProperty(name = "catId", value = "CatId")
+    private String catId;
+
+     /**
+     * 状态:1  有效  -1  无效
+     * 
+     */
+    @Column(name = "status")
+    @ApiModelProperty(name = "status", value = "状态:1  有效  -1  无效")
+    private Integer status;
+
+     /**
+     * 创建时间
+     * 
+     */
+    @Column(name = "created")
+    @ApiModelProperty(name = "created", value = "创建时间")
+    private Date created;
+
+     /**
+     * 修改时间
+     * 
+     */
+    @Column(name = "updated")
+    @ApiModelProperty(name = "updated", value = "修改时间")
+    private Date updated;
+
+     /**
+     * 创建者
+     * 
+     */
+    @Column(name = "creator")
+    @ApiModelProperty(name = "creator", value = "创建者")
+    private String creator;
+
+     /**
+     * 修改者
+     * 
+     */
+    @Column(name = "updator")
+    @ApiModelProperty(name = "updator", value = "修改者")
+    private String updator;
+
+
+     /**
+     * 获取: Id
+     * 
+     */
+    public Integer getId() {
+    return id;
+    }
+     /**
+     * 设置: Id
+     * 
+     */
+    public void setId(Integer id) {
+    this.id = id;
+    }
+     /**
+     * 获取: ProjectName
+     * 
+     */
+    public String getProjectName() {
+    return projectName;
+    }
+     /**
+     * 设置: ProjectName
+     * 
+     */
+    public void setProjectName(String projectName) {
+    this.projectName = projectName;
+    }
+     /**
+     * 获取: Url
+     * 
+     */
+    public String getUrl() {
+    return url;
+    }
+     /**
+     * 设置: Url
+     * 
+     */
+    public void setUrl(String url) {
+    this.url = url;
+    }
+     /**
+     * 获取: 请求状态 1 有效 0 超时 -1 服务连接不上
+     * 
+     */
+    public Integer getReqStatus() {
+    return reqStatus;
+    }
+     /**
+     * 设置: 请求状态 1 有效 0 超时 -1 服务连接不上
+     * 
+     */
+    public void setReqStatus(Integer reqStatus) {
+    this.reqStatus = reqStatus;
+    }
+     /**
+     * 获取: 样本计算总数
+     * 
+     */
+    public Integer getSimpleCount() {
+    return simpleCount;
+    }
+     /**
+     * 设置: 样本计算总数
+     * 
+     */
+    public void setSimpleCount(Integer simpleCount) {
+    this.simpleCount = simpleCount;
+    }
+     /**
+     * 获取: 失败重试总数
+     * 
+     */
+    public Integer getRecoveryRetryCount() {
+    return recoveryRetryCount;
+    }
+     /**
+     * 设置: 失败重试总数
+     * 
+     */
+    public void setRecoveryRetryCount(Integer recoveryRetryCount) {
+    this.recoveryRetryCount = recoveryRetryCount;
+    }
+     /**
+     * 获取: 恢复(冷却)时间
+     * 
+     */
+    public Date getRecoveryTime() {
+    return recoveryTime;
+    }
+     /**
+     * 设置: 恢复(冷却)时间
+     * 
+     */
+    public void setRecoveryTime(Date recoveryTime) {
+    this.recoveryTime = recoveryTime;
+    }
+     /**
+     * 获取: CatId
+     * 
+     */
+    public String getCatId() {
+    return catId;
+    }
+     /**
+     * 设置: CatId
+     * 
+     */
+    public void setCatId(String catId) {
+    this.catId = catId;
+    }
+     /**
+     * 获取: 状态:1  有效  -1  无效
+     * 
+     */
+    public Integer getStatus() {
+    return status;
+    }
+     /**
+     * 设置: 状态:1  有效  -1  无效
+     * 
+     */
+    public void setStatus(Integer status) {
+    this.status = status;
+    }
+     /**
+     * 获取: 创建时间
+     * 
+     */
+    public Date getCreated() {
+    return created;
+    }
+     /**
+     * 设置: 创建时间
+     * 
+     */
+    public void setCreated(Date created) {
+    this.created = created;
+    }
+     /**
+     * 获取: 修改时间
+     * 
+     */
+    public Date getUpdated() {
+    return updated;
+    }
+     /**
+     * 设置: 修改时间
+     * 
+     */
+    public void setUpdated(Date updated) {
+    this.updated = updated;
+    }
+     /**
+     * 获取: 创建者
+     * 
+     */
+    public String getCreator() {
+    return creator;
+    }
+     /**
+     * 设置: 创建者
+     * 
+     */
+    public void setCreator(String creator) {
+    this.creator = creator;
+    }
+     /**
+     * 获取: 修改者
+     * 
+     */
+    public String getUpdator() {
+    return updator;
+    }
+     /**
+     * 设置: 修改者
+     * 
+     */
+    public void setUpdator(String updator) {
+    this.updator = updator;
+    }
+}

+ 2 - 2
elab-spring/src/main/java/com/elab/spring/exception/CommonException.java

@@ -90,7 +90,7 @@ public class CommonException {
     @ResponseBody
     @ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED)
     public Object notFondMethod(Exception ex) {
-        logger.info(" 全局notFondMethod异常捕获 .." + ex.getMessage());
+        logger.debug(" 全局notFondMethod异常捕获 .." + ex.getMessage());
         // 将匹配不到方法的异常状态定位404。
         Info info = getInfo(defaultNotFoundError, "not fond");
         return info;
@@ -100,7 +100,7 @@ public class CommonException {
     @ResponseBody
     @ResponseStatus(value = HttpStatus.NOT_FOUND)
     public Object notReadable(Exception ex) {
-        logger.info(" 全局notFondMethod异常捕获 .." + ex.getMessage());
+        logger.debug(" 全局notFondMethod异常捕获 .." + ex.getMessage());
         // 将参数有异常的数据拦截返回
         Info info = getInfo(defaultNotFoundError, "请检查请求数据..");
         return info;

+ 14 - 0
elab-spring/src/main/java/com/elab/spring/exception/rest/CoolingException.java

@@ -0,0 +1,14 @@
+package com.elab.spring.exception.rest;
+
+/**
+ * 冷却中异常
+ *
+ * @author : liukx
+ * @time : 2020/6/17 - 17:52
+ */
+public class CoolingException extends Exception {
+
+    public CoolingException(String message) {
+        super(message);
+    }
+}

+ 138 - 0
elab-spring/src/main/java/com/elab/spring/factory/HttpsClientRequestFactory.java

@@ -0,0 +1,138 @@
+package com.elab.spring.factory;
+
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+
+import javax.net.ssl.*;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.cert.X509Certificate;
+
+/**
+ * 声明:此代码摘录自https://blog.csdn.net/wltsysterm/article/details/80977455
+ * 声明:关于Socket的相关知识,本人会在后面的闲暇时间进行学习整理,请持续关注博客更新
+ *
+ * @author JustryDeng
+ * @DATE 2018年9月8日 下午4:34:02
+ */
+public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {
+
+    @Override
+    protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
+        try {
+            if (!(connection instanceof HttpsURLConnection)) {
+                super.prepareConnection(connection, httpMethod);
+                // throw new RuntimeException("An instance of HttpsURLConnection is expected");
+                return;
+            }
+
+            HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
+
+            TrustManager[] trustAllCerts = new TrustManager[]{
+                    new X509TrustManager() {
+                        @Override
+                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+                            return null;
+                        }
+
+                        @Override
+                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
+                        }
+
+                        @Override
+                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
+                        }
+
+                    }
+            };
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+//            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
+            sslContext.init(null, trustAllCerts, null);
+            httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
+            SSLContext.setDefault(sslContext);
+            httpsConnection.setHostnameVerifier(new HostnameVerifier() {
+                @Override
+                public boolean verify(String s, SSLSession sslSession) {
+                    return true;
+                }
+            });
+
+            super.prepareConnection(httpsConnection, httpMethod);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
+     * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section)
+     */
+    // SSLSocketFactory用于创建 SSLSockets
+    private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
+
+        private final SSLSocketFactory delegate;
+
+        public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
+            this.delegate = delegate;
+        }
+
+        // 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。
+        // 这些默认的服务的最低质量要求保密保护和服务器身份验证
+        @Override
+        public String[] getDefaultCipherSuites() {
+            return delegate.getDefaultCipherSuites();
+        }
+
+        // 返回的密码套件可用于SSL连接启用的名字
+        @Override
+        public String[] getSupportedCipherSuites() {
+            return delegate.getSupportedCipherSuites();
+        }
+
+
+        @Override
+        public Socket createSocket(final Socket socket, final String host, final int port,
+                                   final boolean autoClose) throws IOException {
+            final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
+            return overrideProtocol(underlyingSocket);
+        }
+
+
+        @Override
+        public Socket createSocket(final String host, final int port) throws IOException {
+            final Socket underlyingSocket = delegate.createSocket(host, port);
+            return overrideProtocol(underlyingSocket);
+        }
+
+        @Override
+        public Socket createSocket(final String host, final int port, final InetAddress localAddress,
+                                   final int localPort) throws
+                IOException {
+            final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
+            return overrideProtocol(underlyingSocket);
+        }
+
+        @Override
+        public Socket createSocket(final InetAddress host, final int port) throws IOException {
+            final Socket underlyingSocket = delegate.createSocket(host, port);
+            return overrideProtocol(underlyingSocket);
+        }
+
+        @Override
+        public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress,
+                                   final int localPort) throws
+                IOException {
+            final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
+            return overrideProtocol(underlyingSocket);
+        }
+
+        private Socket overrideProtocol(final Socket socket) {
+            if (!(socket instanceof SSLSocket)) {
+                throw new RuntimeException("An instance of SSLSocket is expected");
+            }
+            ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"});
+            return socket;
+        }
+    }
+}

+ 40 - 0
elab-spring/src/main/java/com/elab/spring/model/rest/HttpUrlMetric.java

@@ -0,0 +1,40 @@
+package com.elab.spring.model.rest;
+
+import com.elab.spring.dao.entity.MngHttpInfoEntity;
+
+import java.util.concurrent.atomic.LongAdder;
+
+/**
+ * http请求指标
+ *
+ * @author : liukx
+ * @time : 2020/6/17 - 17:29
+ */
+public class HttpUrlMetric extends MngHttpInfoEntity {
+
+    /**
+     * 成功的总数
+     */
+    private LongAdder okCount = new LongAdder();
+    /**
+     * 失败的总数
+     */
+    private LongAdder failCount = new LongAdder();
+
+    public LongAdder getOkCount() {
+        return okCount;
+    }
+
+    public void setOkCount(LongAdder okCount) {
+        this.okCount = okCount;
+    }
+
+    public LongAdder getFailCount() {
+        return failCount;
+    }
+
+    public void setFailCount(LongAdder failCount) {
+        this.failCount = failCount;
+    }
+
+}

+ 82 - 11
elab-spring/src/main/java/com/elab/spring/utils/RestTemplateUtils.java

@@ -7,6 +7,9 @@ import com.dianping.cat.message.Transaction;
 import com.elab.core.utils.ObjectUtils;
 import com.elab.core.utils.StringUtils;
 import com.elab.log.utils.CatMsgConstants;
+import com.elab.spring.callback.IRestFallback;
+import com.elab.spring.exception.rest.CoolingException;
+import com.elab.spring.factory.HttpsClientRequestFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.*;
@@ -30,6 +33,8 @@ public class RestTemplateUtils {
 
     private HttpHeaders headers;
 
+    private IRestFallback restFallback;
+
     private RestTemplate getRestTemplate() {
         return restTemplate;
     }
@@ -47,14 +52,16 @@ public class RestTemplateUtils {
     }
 
     public RestTemplateUtils() {
+        System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
         //复杂构造函数的使用
-        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
+        SimpleClientHttpRequestFactory requestFactory = new HttpsClientRequestFactory();
         // 设置超时
         requestFactory.setConnectTimeout(1000);
         // 读取超时
         requestFactory.setReadTimeout(5000);
 
         //利用复杂构造器可以实现超时设置,内部实际实现为 HttpClient
+//        restTemplate = new RestTemplate(requestFactory);
         restTemplate = new RestTemplate(requestFactory);
 
         //设置HTTP请求头信息,实现编码等
@@ -64,6 +71,10 @@ public class RestTemplateUtils {
         headers.add("Accept", MediaType.APPLICATION_JSON.toString());
     }
 
+    public void setRestFallback(IRestFallback restFallback) {
+        this.restFallback = restFallback;
+    }
+
     /**
      * post数据放送
      *
@@ -77,6 +88,20 @@ public class RestTemplateUtils {
         return post(url, headers, reqParam, clazz);
     }
 
+    /**
+     * 执行post请求
+     *
+     * @param url          url
+     * @param reqParam     请求参数
+     * @param clazz        返回类型
+     * @param restFallback 失败回调
+     * @param <T>
+     * @return
+     */
+    public <T> T post(String url, Object reqParam, Class<T> clazz, IRestFallback restFallback) {
+        return post(url, headers, reqParam, clazz, restFallback);
+    }
+
     /**
      * 执行post请求
      *
@@ -88,28 +113,64 @@ public class RestTemplateUtils {
      * @return
      */
     public <T> T post(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz) {
+        return post(url, httpHeaders, reqParam, clazz, this.restFallback);
+    }
+
+    /**
+     * post请求最终发送
+     *
+     * @param url          路径
+     * @param httpHeaders  请求头
+     * @param reqParam     请求参数
+     * @param clazz        结果返回对象
+     * @param restFallback 失败回调方法
+     * @param <T>
+     * @return
+     */
+    public <T> T post(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz, IRestFallback<?> restFallback) {
         String newUrl = getUrl(url);
         Transaction t = Cat.getProducer().newTransaction(CatMsgConstants.THIRD_PARTY, newUrl);
-        logger.info(" URL : " + url);
+        logger.debug(" URL : " + url);
         if (reqParam != null) {
-            logger.info(" RequestData : " + StringUtils.logOut(logData(reqParam)));
+            logger.debug(" RequestData : " + StringUtils.logOut(logData(reqParam)));
         }
         T responseData = null;
         try {
             //利用容器实现数据封装,发送
             HttpEntity<Object> entity = new HttpEntity<Object>(reqParam, httpHeaders);
+            requestBefore(url, httpHeaders, reqParam, clazz, restFallback);
             responseData = restTemplate.postForObject(url, entity, clazz);
             t.setStatus(Transaction.SUCCESS);
             logResponse(responseData);
+            requestAfter(url, httpHeaders, reqParam, clazz, restFallback);
         } catch (Exception e) {
-            logger.error("------ 第三方接口调用失败 : ", e);
+            e.printStackTrace();
+            if (!(e instanceof CoolingException)) {
+                logger.error("第三方调用异常:", e);
+            }
             t.setStatus(e.getClass().getSimpleName());
+            if (restFallback != null) {
+                logger.debug("触发异常回调 : " + restFallback.toString());
+                return (T) restFallback.post(url, httpHeaders, reqParam, clazz, e);
+            }
         } finally {
             t.complete();
         }
         return responseData;
     }
 
+    private <T> void requestAfter(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz, IRestFallback<?> restFallback) throws Exception {
+        if (restFallback != null) {
+            restFallback.requestAfter(url, httpHeaders, reqParam, clazz);
+        }
+    }
+
+    private <T> void requestBefore(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz, IRestFallback<?> restFallback) throws Exception {
+        if (restFallback != null) {
+            restFallback.requestBefore(url, httpHeaders, reqParam, clazz);
+        }
+    }
+
     /**
      * 通用的返回日志
      *
@@ -118,9 +179,9 @@ public class RestTemplateUtils {
      */
     private <T> void logResponse(T responseData) {
         if (dontCareType(responseData)) {
-            logger.info(" 不关心的类型 ... ");
+            logger.debug(" 不关心的类型 ... ");
         } else {
-            logger.info(" ResponseData : " + StringUtils.logOut(logData(responseData)));
+            logger.debug(" ResponseData : " + StringUtils.logOut(logData(responseData)));
         }
     }
 
@@ -133,9 +194,13 @@ public class RestTemplateUtils {
      * @return
      */
     public <T> T get(String url, Class<T> clazz) {
+        return get(url, clazz, this.restFallback);
+    }
+
+    public <T> T get(String url, Class<T> clazz, IRestFallback restFallback) {
         String newUrl = getUrl(url);
         Transaction t = Cat.getProducer().newTransaction(CatMsgConstants.THIRD_PARTY, newUrl);
-        logger.info(" URL : " + url);
+        logger.debug(" URL : " + url);
         //利用容器实现数据封装,发送
         T responseData = null;
         try {
@@ -145,8 +210,14 @@ public class RestTemplateUtils {
                 logResponse((T) responseData);
             }
         } catch (Exception e) {
-            logger.error("------ 第三方接口调用失败 : ", e);
+            if (!(e instanceof CoolingException)) {
+                logger.error("第三方调用异常:", e);
+            }
             t.setStatus(e.getClass().getSimpleName());
+            if (restFallback != null) {
+                logger.debug("触发失败回调 : " + restFallback.toString());
+                return (T) restFallback.get(url, clazz, e);
+            }
         } finally {
             t.complete();
         }
@@ -167,8 +238,8 @@ public class RestTemplateUtils {
      * @return
      */
     public <T> T postUpload(String url, Object reqParam, Class<T> clazz) {
-        logger.info(" URL : " + url);
-        logger.info(" RequestData : " + ObjectUtils.objectParseJsonStr(reqParam));
+        logger.debug(" URL : " + url);
+        logger.debug(" RequestData : " + ObjectUtils.objectParseJsonStr(reqParam));
 
         String newUrl = getUrl(url);
         Transaction t = Cat.getProducer().newTransaction(CatMsgConstants.THIRD_PARTY, newUrl);
@@ -179,7 +250,7 @@ public class RestTemplateUtils {
             ResponseEntity<T> exchange = restTemplate.exchange(url, HttpMethod.POST, entity, clazz);
             responseData = exchange.getBody();
             if (exchange != null && responseData != null) {
-                logger.info(" ResponseData : " + StringUtils.logOut(logData(responseData)));
+                logger.debug(" ResponseData : " + StringUtils.logOut(logData(responseData)));
             }
             t.setStatus(Transaction.SUCCESS);
         } catch (Exception e) {

+ 63 - 1
elab-spring/src/test/java/com/elab/spring/utils/RestTemplateUtilsTest.java

@@ -1,9 +1,20 @@
 package com.elab.spring.utils;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.elab.core.aop.annotations.EnableElabDB;
 import com.elab.core.utils.GzipUtils;
 import com.elab.core.utils.StringUtils;
+import com.elab.spring.callback.IRestFallback;
+import com.elab.spring.callback.impl.DefaultRestFallBack;
+import com.elab.spring.dao.MngHttpFailureDataDao;
+import com.elab.spring.dao.entity.MngHttpInfoEntity;
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.web.client.RestTemplate;
 
 import java.io.*;
@@ -11,20 +22,50 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.zip.GZIPInputStream;
 
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {DefaultRestFallBack.class})
+@EnableElabDB(basePackageClasses = {MngHttpFailureDataDao.class, MngHttpInfoEntity.class})
 public class RestTemplateUtilsTest {
 
+
+    @Autowired
+    private IRestFallback restFallBack;
+
     private RestTemplateUtils restTemplateUtils = new RestTemplateUtils();
 
     private RestTemplate restTemplate = new RestTemplate();
 
     @Test
     public void post() {
-        String url = "http://106.14.133.2:5555/elab-marketing-authentication//ipAddr/getIpAddr";
+        String url = "http://106.14.133.2:5555/elab-marketing-user//ipAddr/getIpAddr";
         String post = restTemplateUtils.post(url, null, String.class);
         System.out.println("--->" + post);
         Assert.assertNotNull(post);
     }
 
+    @Test
+    public void httpsPost() {
+        String url = "https://my.jianye.com.cn:19051/MIPApiAuth/Token";
+        String data = "{\n" +
+                "\t\"client_id\": \"建业云平台\",\n" +
+                "\t\"client_secret\": \"fbc670f03fd2bd0\"\n" +
+                "}";
+        JSONObject jsonObject = JSON.parseObject(data);
+        String post = restTemplateUtils.post(url, jsonObject, String.class);
+        System.out.println("--->" + post);
+        Assert.assertNotNull(post);
+    }
+
+    @Test
+    public void httpsPost2() {
+        String url = "https://apit.centralchina.com:9443/env-101/por-ext/jy001/infos/handler?apikey=OluJj80Kne2koiSMIdX0kfCKwfpQa6LB";
+        String data = "{\"AppKey\":\"5c4dee0c0752e0acc5fd6da1ad34321d\",\"PostData\":[{\"Consumer\":\"测试用户\",\"PrjName\":\"建业橙园\",\"Phone\":\"12399009988\",\"Guid\":\"93A92CBE-497E-E711-8ED9-0025907D1F8D\",\"Place\":\"建业云\",\"Create_Time\":\"2020-06-18 11:17:19\"}]}";
+        JSONObject jsonObject = JSON.parseObject(data);
+        String post = restTemplateUtils.post(url, jsonObject, String.class);
+        System.out.println("--->" + post);
+        Assert.assertNotNull(post);
+    }
+
     @Test
     public void get() {
         String url = "http://www.baidu.com";
@@ -57,6 +98,27 @@ public class RestTemplateUtilsTest {
 
     }
 
+    @Test
+    public void httpFallBack() throws Exception {
+        restTemplateUtils.setRestFallback(restFallBack);
+//        String url = "https://my.jianye.com.cn:19051/MIPApiAuth/Token";
+//        String data = "{\n" +
+//                "\t\"client_id\": \"建业云平台\",\n" +
+//                "\t\"client_secret\": \"fbc670f03fd2bd0\"\n" +
+//                "}";
+        String url = "https://elab-lkx.utools.club/zhidi/info/list";
+        String data = "{\n" +
+                "    \n" +
+                "    \"id\": 44\n" +
+                "    \n" +
+                "}";
+        JSONObject jsonObject = JSON.parseObject(data);
+        for (int i = 0; i < 10; i++) {
+            String post = restTemplateUtils.post(url, jsonObject, String.class);
+            System.out.println("--->" + post);
+        }
+    }
+
     @Test
     public void test1() {
         String array = "2610\n" +

+ 0 - 0
elab-spring/src/test/resources/application-dev.yml


Некоторые файлы не были показаны из-за большого количества измененных файлов