cuixiaodong 10 ماه پیش
والد
کامیت
d6f49b371f
100فایلهای تغییر یافته به همراه8158 افزوده شده و 0 حذف شده
  1. 464 0
      framework/pom.xml
  2. 109 0
      framework/src/main/java/com/hys/app/framework/ErpConfig.java
  3. 29 0
      framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenCache.java
  4. 32 0
      framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenGetter.java
  5. 36 0
      framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenGetterFactory.java
  6. 33 0
      framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenIdentifier.java
  7. 26 0
      framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenManager.java
  8. 97 0
      framework/src/main/java/com/hys/app/framework/apptoken/impl/AccessTokenCacheDefaultImpl.java
  9. 88 0
      framework/src/main/java/com/hys/app/framework/apptoken/impl/AccessTokenManagerImpl.java
  10. 50 0
      framework/src/main/java/com/hys/app/framework/apptoken/model/AccessToken.java
  11. 17 0
      framework/src/main/java/com/hys/app/framework/apptoken/model/AppTypeEnum.java
  12. 17 0
      framework/src/main/java/com/hys/app/framework/auth/AuthUser.java
  13. 88 0
      framework/src/main/java/com/hys/app/framework/auth/BeanUtil.java
  14. 43 0
      framework/src/main/java/com/hys/app/framework/auth/Token.java
  15. 20 0
      framework/src/main/java/com/hys/app/framework/auth/TokenCreater.java
  16. 15 0
      framework/src/main/java/com/hys/app/framework/auth/TokenParseException.java
  17. 20 0
      framework/src/main/java/com/hys/app/framework/auth/TokenParser.java
  18. 33 0
      framework/src/main/java/com/hys/app/framework/auth/auth.uml
  19. 99 0
      framework/src/main/java/com/hys/app/framework/auth/impl/JwtTokenCreater.java
  20. 87 0
      framework/src/main/java/com/hys/app/framework/auth/impl/JwtTokenParser.java
  21. 139 0
      framework/src/main/java/com/hys/app/framework/cache/Cache.java
  22. 163 0
      framework/src/main/java/com/hys/app/framework/cache/MybatisRedisCache.java
  23. 137 0
      framework/src/main/java/com/hys/app/framework/cache/RedisCacheImpl.java
  24. 68 0
      framework/src/main/java/com/hys/app/framework/context/ApplicationContextHolder.java
  25. 117 0
      framework/src/main/java/com/hys/app/framework/context/WebInterceptorConfigurer.java
  26. 25 0
      framework/src/main/java/com/hys/app/framework/context/app/AppTypeContext.java
  27. 33 0
      framework/src/main/java/com/hys/app/framework/context/app/AppTypeEnum.java
  28. 108 0
      framework/src/main/java/com/hys/app/framework/context/instance/AppInstance.java
  29. 13 0
      framework/src/main/java/com/hys/app/framework/context/instance/HeartBeatProxy.java
  30. 33 0
      framework/src/main/java/com/hys/app/framework/context/instance/InstanceContext.java
  31. 186 0
      framework/src/main/java/com/hys/app/framework/context/instance/InstanceContextImpl.java
  32. 28 0
      framework/src/main/java/com/hys/app/framework/context/instance/ScheduledConfig.java
  33. 51 0
      framework/src/main/java/com/hys/app/framework/context/params/JacksonConfig.java
  34. 89 0
      framework/src/main/java/com/hys/app/framework/context/params/SnakeToCamelArgumentResolver.java
  35. 56 0
      framework/src/main/java/com/hys/app/framework/context/params/SnakeToCamelModelAttributeMethodProcessor.java
  36. 73 0
      framework/src/main/java/com/hys/app/framework/context/params/SnakeToCamelRequestDataBinder.java
  37. 152 0
      framework/src/main/java/com/hys/app/framework/context/request/HysRequestInterceptor.java
  38. 48 0
      framework/src/main/java/com/hys/app/framework/context/request/Shop.java
  39. 26 0
      framework/src/main/java/com/hys/app/framework/context/request/ShopContextHolder.java
  40. 49 0
      framework/src/main/java/com/hys/app/framework/context/request/ThreadContextHolder.java
  41. 151 0
      framework/src/main/java/com/hys/app/framework/context/user/AdminUserContext.java
  42. 33 0
      framework/src/main/java/com/hys/app/framework/context/user/ChainUserContext.java
  43. 48 0
      framework/src/main/java/com/hys/app/framework/context/user/DefaultUserHolder.java
  44. 42 0
      framework/src/main/java/com/hys/app/framework/context/user/ParameterServletRequestWrapper.java
  45. 57 0
      framework/src/main/java/com/hys/app/framework/context/user/SnakeCaseInterceptor.java
  46. 74 0
      framework/src/main/java/com/hys/app/framework/context/user/UserContext.java
  47. 28 0
      framework/src/main/java/com/hys/app/framework/context/user/UserHolder.java
  48. 60 0
      framework/src/main/java/com/hys/app/framework/database/BaseQueryParam.java
  49. 53 0
      framework/src/main/java/com/hys/app/framework/database/ColumnMeta.java
  50. 37 0
      framework/src/main/java/com/hys/app/framework/database/DBRuntimeException.java
  51. 271 0
      framework/src/main/java/com/hys/app/framework/database/DaoSupport.java
  52. 56 0
      framework/src/main/java/com/hys/app/framework/database/DataMeta.java
  53. 200 0
      framework/src/main/java/com/hys/app/framework/database/DataSourceConfig.java
  54. 24 0
      framework/src/main/java/com/hys/app/framework/database/DoubleMapper.java
  55. 29 0
      framework/src/main/java/com/hys/app/framework/database/DynamicField.java
  56. 23 0
      framework/src/main/java/com/hys/app/framework/database/LongMapper.java
  57. 25 0
      framework/src/main/java/com/hys/app/framework/database/ObjectNotFoundException.java
  58. 42 0
      framework/src/main/java/com/hys/app/framework/database/SqlMetaBuilder.java
  59. 24 0
      framework/src/main/java/com/hys/app/framework/database/StringMapper.java
  60. 148 0
      framework/src/main/java/com/hys/app/framework/database/WebPage.java
  61. 21 0
      framework/src/main/java/com/hys/app/framework/database/annotation/Column.java
  62. 17 0
      framework/src/main/java/com/hys/app/framework/database/annotation/Id.java
  63. 17 0
      framework/src/main/java/com/hys/app/framework/database/annotation/NotDbField.java
  64. 17 0
      framework/src/main/java/com/hys/app/framework/database/annotation/PrimaryKeyField.java
  65. 21 0
      framework/src/main/java/com/hys/app/framework/database/annotation/Table.java
  66. 559 0
      framework/src/main/java/com/hys/app/framework/database/impl/DaoSupportImpl.java
  67. 39 0
      framework/src/main/java/com/hys/app/framework/database/impl/FilterColumnMapRowMapper.java
  68. 25 0
      framework/src/main/java/com/hys/app/framework/database/impl/IRowMapperColumnFilter.java
  69. 21 0
      framework/src/main/java/com/hys/app/framework/database/impl/LowerCaseJdbcTemplate.java
  70. 39 0
      framework/src/main/java/com/hys/app/framework/database/impl/MySqlColumnMapRowMapper.java
  71. 126 0
      framework/src/main/java/com/hys/app/framework/database/impl/MySqlMetaBuilderImpl.java
  72. 52 0
      framework/src/main/java/com/hys/app/framework/database/impl/OracleColumnMapRowMapper.java
  73. 42 0
      framework/src/main/java/com/hys/app/framework/database/impl/SqlPaser.java
  74. 64 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/DefaultDBFieldHandler.java
  75. 18 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/MybatisAutoConfiguration.java
  76. 46 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/base/BaseDO.java
  77. 157 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/base/BaseMapperX.java
  78. 72 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/base/BaseService.java
  79. 16 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/base/BaseServiceImpl.java
  80. 56 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/type/LongListTypeHandler.java
  81. 55 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/type/StringListTypeHandler.java
  82. 180 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/LambdaQueryChainWrapperX.java
  83. 135 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/LambdaQueryWrapperX.java
  84. 319 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/MPJLambdaWrapperX.java
  85. 170 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/QueryChainWrapperX.java
  86. 140 0
      framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/QueryWrapperX.java
  87. 36 0
      framework/src/main/java/com/hys/app/framework/datapermission/annotation/DataPermission.java
  88. 36 0
      framework/src/main/java/com/hys/app/framework/datapermission/aop/DataPermissionAnnotationAdvisor.java
  89. 73 0
      framework/src/main/java/com/hys/app/framework/datapermission/aop/DataPermissionAnnotationInterceptor.java
  90. 65 0
      framework/src/main/java/com/hys/app/framework/datapermission/aop/DataPermissionContextHolder.java
  91. 44 0
      framework/src/main/java/com/hys/app/framework/datapermission/config/DataPermissionConfiguration.java
  92. 640 0
      framework/src/main/java/com/hys/app/framework/datapermission/db/DataPermissionDatabaseInterceptor.java
  93. 36 0
      framework/src/main/java/com/hys/app/framework/datapermission/rule/DataPermissionRule.java
  94. 28 0
      framework/src/main/java/com/hys/app/framework/datapermission/rule/DataPermissionRuleFactory.java
  95. 62 0
      framework/src/main/java/com/hys/app/framework/datapermission/rule/DataPermissionRuleFactoryImpl.java
  96. 43 0
      framework/src/main/java/com/hys/app/framework/datapermission/util/DataPermissionUtils.java
  97. 51 0
      framework/src/main/java/com/hys/app/framework/elasticsearch/EsConfig.java
  98. 56 0
      framework/src/main/java/com/hys/app/framework/elasticsearch/EsIndexInit.java
  99. 84 0
      framework/src/main/java/com/hys/app/framework/elasticsearch/EsMappingUtil.java
  100. 58 0
      framework/src/main/java/com/hys/app/framework/elasticsearch/EsSettings.java

+ 464 - 0
framework/pom.xml

@@ -0,0 +1,464 @@
+<?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>warehouse</artifactId>
+        <groupId>com.hys.app</groupId>
+        <version>7.2.2</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>framework</artifactId>
+
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
+            <artifactId>owasp-java-html-sanitizer</artifactId>
+            <version>r136</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.11.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+            <version>1.1.22</version>
+        </dependency>
+        <dependency>
+            <groupId>io.seata</groupId>
+            <artifactId>seata-spring-boot-starter</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.8.2</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+            <version>1.8.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.10</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>2.6.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.7.0</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>2.8.0</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>2.8.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-bean-validators</artifactId>
+            <version>2.8.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+            <version>2.9.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.commons</groupId>
+                    <artifactId>commons-pool2</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+            <version>2.5.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjweaver</artifactId>
+            <version>1.8.9</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+            <version>3.5.5</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hibernate.validator</groupId>
+            <artifactId>hibernate-validator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant</artifactId>
+            <version>1.10.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.6</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.sun.mail</groupId>
+            <artifactId>javax.mail</artifactId>
+            <version>1.6.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>2.18.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>3.17</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>3.17</version>
+        </dependency>
+        <dependency>
+            <groupId>org.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+            <version>3.8.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-freemarker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.janino</groupId>
+            <artifactId>janino</artifactId>
+            <version>3.0.7</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.shardingsphere</groupId>
+            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
+            <version>${shardingsphere.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.5.4</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alipay.sdk</groupId>
+            <artifactId>alipay-easysdk</artifactId>
+            <version>2.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+            <version>8.0.3</version>
+        </dependency>
+        <!--加入log4j2依赖-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-slf4j-impl</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- 2.17.0修复漏洞版本 -->
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <version>${log4j2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <version>${log4j2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <version>${log4j2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-spring-boot</artifactId>
+            <version>${log4j2.version}</version>
+        </dependency>
+
+        <!--websocket-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-slf4j-impl</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-to-slf4j</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+            <version>1.5.5.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
+            <version>1.5.5.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
+            <version>1.5.5.Final</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.yulichang</groupId>
+            <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
+            <version>1.4.6</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.3.5</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>transmittable-thread-local</artifactId>
+            <version>2.14.2</version>
+        </dependency>
+
+
+        <!-- todo 以下是从erp-core拿过来的,暂时先放这里 -->
+
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jsch</artifactId>
+            <version>0.1.53</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.integration</groupId>
+            <artifactId>spring-integration-http</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>core</artifactId>
+            <version>3.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>javase</artifactId>
+            <version>3.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.barcode4j</groupId>
+            <artifactId>barcode4j-light</artifactId>
+            <version>2.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.belerweb</groupId>
+            <artifactId>pinyin4j</artifactId>
+            <version>2.5.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>23.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.xfire</groupId>
+            <artifactId>xfire-core</artifactId>
+            <version>1.2.6</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk16</artifactId>
+            <version>1.46</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+            <version>2.2.5</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-afs</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <!-- API, java.xml.bind module -->
+        <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+            <version>2.3.2</version>
+        </dependency>
+        <dependency>
+            <groupId>net.bytebuddy</groupId>
+            <artifactId>byte-buddy</artifactId>
+            <version>1.11.13</version>
+        </dependency>
+        <dependency>
+            <groupId>net.bytebuddy</groupId>
+            <artifactId>byte-buddy-agent</artifactId>
+            <version>1.11.13</version>
+            <scope>test</scope>
+        </dependency>
+        <!-- Runtime, com.sun.xml.bind module -->
+        <dependency>
+            <groupId>org.glassfish.jaxb</groupId>
+            <artifactId>jaxb-runtime</artifactId>
+            <version>2.3.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dysmsapi20170525</artifactId>
+            <version>2.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>3.0.5</version>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>docker-maven-plugin</artifactId>
+                <version>1.1.1</version>
+                <configuration>
+                    <skipDocker>true</skipDocker>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+
+</project>

+ 109 - 0
framework/src/main/java/com/hys/app/framework/ErpConfig.java

@@ -0,0 +1,109 @@
+package com.hys.app.framework;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 配置
+ *
+ * @author zh
+ * @version v7.0
+ * @date 18/4/13 下午8:19
+ * @since v7.0
+ */
+@Configuration
+@ConfigurationProperties(prefix = "erp")
+@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
+@SuppressWarnings("ConfigurationProperties")
+public class ErpConfig {
+
+    /**
+     * token加密秘钥
+     */
+    private String tokenSecret;
+
+    /**
+     * 访问token失效时间
+     */
+    @Value("${erp.timeout.accessTokenTimeout:#{null}}")
+    private Integer accessTokenTimeout;
+
+    @Value("${erp.timeout.refreshTokenTimeout:#{null}}")
+    private Integer refreshTokenTimeout;
+
+    @Value("${erp.timeout.captchaTimout:#{null}}")
+    private Integer captchaTimout;
+
+    @Value("${erp.timeout.smscodeTimout:#{null}}")
+    private Integer smscodeTimout;
+
+    @Value("${erp.debugger:#{false}}")
+    private boolean debugger;
+
+
+    public ErpConfig() {
+    }
+
+
+    @Override
+    public String toString() {
+        return "erpConfig{" +
+                "accessTokenTimeout=" + accessTokenTimeout +
+                ", refreshTokenTimeout=" + refreshTokenTimeout +
+                ", captchaTimout=" + captchaTimout +
+                ", smscodeTimout=" + smscodeTimout +
+                '}';
+    }
+
+    public String getTokenSecret() {
+        return tokenSecret;
+    }
+
+    public void setTokenSecret(String tokenSecret) {
+        this.tokenSecret = tokenSecret;
+    }
+
+    public Integer getAccessTokenTimeout() {
+        return accessTokenTimeout;
+    }
+
+    public void setAccessTokenTimeout(Integer accessTokenTimeout) {
+        this.accessTokenTimeout = accessTokenTimeout;
+    }
+
+    public Integer getRefreshTokenTimeout() {
+        return refreshTokenTimeout;
+    }
+
+    public void setRefreshTokenTimeout(Integer refreshTokenTimeout) {
+        this.refreshTokenTimeout = refreshTokenTimeout;
+    }
+
+    public Integer getCaptchaTimout() {
+        return captchaTimout;
+    }
+
+    public void setCaptchaTimout(Integer captchaTimout) {
+        this.captchaTimout = captchaTimout;
+    }
+
+    public Integer getSmscodeTimout() {
+        return smscodeTimout;
+    }
+
+    public void setSmscodeTimout(Integer smscodeTimout) {
+        this.smscodeTimout = smscodeTimout;
+    }
+
+    public boolean isDebugger() {
+        return debugger;
+    }
+
+    public void setDebugger(boolean debugger) {
+        this.debugger = debugger;
+    }
+
+}

+ 29 - 0
framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenCache.java

@@ -0,0 +1,29 @@
+package com.hys.app.framework.apptoken;
+
+import com.hys.app.framework.apptoken.model.AccessToken;
+
+/**
+ * access token缓存接口<br/>
+ * 专门面向access token的缓存
+ * @author kingapex
+ * @data 2021/8/29 13:36
+ * @version 1.0
+ **/
+public interface AccessTokenCache {
+
+    /**
+     * 向缓存中压入token
+     * @param accessTokenIdentifier token的唯一标识
+     * @param accessToken 要缓存的token
+     */
+    void put(AccessTokenIdentifier accessTokenIdentifier, AccessToken accessToken);
+
+    /**
+     * 由缓存中获取token
+     * @param accessTokenIdentifier token的唯一标识
+     * @return 缓存的token
+     */
+    AccessToken get(AccessTokenIdentifier accessTokenIdentifier);
+
+
+}

+ 32 - 0
framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenGetter.java

@@ -0,0 +1,32 @@
+package com.hys.app.framework.apptoken;
+
+import com.hys.app.framework.apptoken.model.AccessToken;
+import com.hys.app.framework.apptoken.model.AppTypeEnum;
+
+/**
+ * access token获取器<br/>
+ * 1、为了统一对外提供token的获取接口<br/>
+ * 2、兼容各种第三方应用access token获取的情况<br/>
+ * 3、提供统一的token缓存管理
+ *
+ * @author kingapex
+ * @data 2021/8/29 11:51
+ * @version 1.0
+ **/
+public interface AccessTokenGetter {
+
+    /**
+     * 由第三方服务api获取token
+     * @param accessTokenIdentifier token唯一标识
+     * @return
+     */
+    AccessToken getToken(AccessTokenIdentifier accessTokenIdentifier);
+
+    /**
+     * 定一个这个token getter的唯一凭证
+     * 是和AccesssTokenAppEnum 一一对应的,
+     * 也就是说有一个第三方应用,就有一个token getter
+     * @return
+     */
+    AppTypeEnum id();
+}

+ 36 - 0
framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenGetterFactory.java

@@ -0,0 +1,36 @@
+package com.hys.app.framework.apptoken;
+
+import com.hys.app.framework.apptoken.model.AppTypeEnum;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * AccessTokenGetter Bean工厂
+ * 根据AccesssTokenAppEnum获取唯一的 TokenGetter
+ * @author kingapex
+ * @version 1.0
+ * @description TODO
+ * @data 2021/8/29 13:03
+ **/
+@Service
+public class AccessTokenGetterFactory {
+
+    @Autowired(required = false)
+    private List<AccessTokenGetter> getterList;
+
+    /**
+     * 根据AccesssTokenAppEnum获取唯一的 TokenGetter
+     * @param appTypeEnum token app类型枚举
+     * @return
+     */
+    public AccessTokenGetter getBean(AppTypeEnum appTypeEnum) {
+        List<AccessTokenGetter> list = getterList.stream().filter(getter -> getter.id().equals(appTypeEnum)).collect(Collectors.toList());
+        if (list.isEmpty()) {
+            return null;
+        }
+        return list.get(0);
+    }
+}

+ 33 - 0
framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenIdentifier.java

@@ -0,0 +1,33 @@
+package com.hys.app.framework.apptoken;
+
+import com.hys.app.framework.apptoken.model.AppTypeEnum;
+
+/**
+ * access token唯一标识符<br/>
+ * 这个标识符会用来识别token getter、缓存token,查找token<br/>
+ * 应该保证其中build id的唯一性<b/>
+ * 这个抽象的标识符,包含了一个AccesssTokenAppEnum,即标识了由哪个token getter获取token.
+ * @author kingapex
+ * @data 2021/8/29 12:37
+ * @version 1.0
+ **/
+public  abstract class AccessTokenIdentifier {
+
+
+    /**
+     * 第三方应用
+     */
+    private AppTypeEnum appTypeEnum;
+
+
+    public AccessTokenIdentifier(AppTypeEnum appTypeEnum) {
+        this.appTypeEnum = appTypeEnum;
+    }
+
+    public AppTypeEnum getAppEnum() {
+        return this.appTypeEnum;
+    }
+
+    public abstract String buildId();
+
+}

+ 26 - 0
framework/src/main/java/com/hys/app/framework/apptoken/AccessTokenManager.java

@@ -0,0 +1,26 @@
+package com.hys.app.framework.apptoken;
+
+/**
+ * access token管理接接口
+ * @author kingapex
+ * @data 2021/8/22 16:26
+ * @version 1.0
+ **/
+public interface AccessTokenManager {
+
+    /**
+     *  获取缓存的token,如果过期会重新刷新token、缓存并返回
+     * @param accessTokenIdentifier token的唯一标识
+     *
+     * @return
+     */
+    String  getToken(AccessTokenIdentifier accessTokenIdentifier);
+
+
+    /**
+     * 强制刷新token
+     * 会调用api重新获取token,刷新token、缓存并返回
+     * @param accessTokenIdentifier token的唯一标识
+     */
+    String refreshToken(AccessTokenIdentifier accessTokenIdentifier);
+}

+ 97 - 0
framework/src/main/java/com/hys/app/framework/apptoken/impl/AccessTokenCacheDefaultImpl.java

@@ -0,0 +1,97 @@
+package com.hys.app.framework.apptoken.impl;
+
+import com.hys.app.framework.apptoken.AccessTokenCache;
+import com.hys.app.framework.apptoken.model.AccessToken;
+import com.hys.app.framework.apptoken.AccessTokenIdentifier;
+import com.hys.app.framework.cache.Cache;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.hys.app.framework.util.DateUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * access token二级缓存的实现
+ * 一级缓存使用本jvm的static变量
+ * 二级缓存使用redis缓存
+ * @author kingapex
+ * @version 1.0
+ * @description TODO
+ * @data 2021/8/23 17:49
+ **/
+@Service
+public class AccessTokenCacheDefaultImpl implements AccessTokenCache {
+
+    @Autowired
+    private Cache redisCache;
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private static Map<String, AccessToken> localCache;
+    static {
+        localCache = new HashMap<>();
+    }
+
+    /**
+     * 先由本地缓存中读取token
+     * 如果不存在或已经失效则由redis中读取,如果有效则存入本地缓存
+     * 最终判断如果token有效则返回,否则返回null
+     * @param accessTokenIdentifier token的唯一标识
+     * @return
+     */
+    @Override
+    public AccessToken get(AccessTokenIdentifier accessTokenIdentifier){
+        String cacheKey = getCacheKey(accessTokenIdentifier);
+        Long now = DateUtil.getDateline();
+
+        //先从本地缓存获取token
+        AccessToken accessToken = localCache.get(cacheKey);
+
+        logger.debug("由本地缓存获取token "+ accessToken);
+
+
+        //不存在或已经失效,则由redis缓存中获取
+        if (accessToken==null || now > accessToken.getExpires()) {
+            accessToken = (AccessToken) redisCache.get(cacheKey);
+            logger.debug("由redis缓存获取token "+ accessToken);
+            //redis缓存中的token不为null 并且有效,则压入本地缓存
+            if (accessToken!=null && now < accessToken.getExpires()) {
+                localCache.put(cacheKey, accessToken);
+            }
+        }
+
+        //如果token有效,则返回,否则返回null
+        if (accessToken!=null &&  now < accessToken.getExpires()) {
+            return accessToken;
+        }
+        return null;
+    }
+
+    /**
+     * 将token同时放入redis缓存和本地缓存
+     * @param accessTokenIdentifier token的唯一标识
+     * @param accessToken 要缓存的token
+     */
+    @Override
+    public void put(AccessTokenIdentifier accessTokenIdentifier, AccessToken accessToken) {
+        String cacheKey = getCacheKey(accessTokenIdentifier);
+        localCache.put(cacheKey, accessToken);
+        redisCache.put(cacheKey, accessToken,accessToken.getExpires().intValue());
+    }
+
+    /**
+     * 获取缓存的key
+     * 本地缓存是一个map,这个key是map中的key
+     * reids缓存使用这个Key作为缓存key
+     * @param accessTokenIdentifier
+     * @return
+     */
+    private String getCacheKey(AccessTokenIdentifier accessTokenIdentifier) {
+
+        return "{access_token}_" + accessTokenIdentifier.buildId();
+    }
+
+}

+ 88 - 0
framework/src/main/java/com/hys/app/framework/apptoken/impl/AccessTokenManagerImpl.java

@@ -0,0 +1,88 @@
+package com.hys.app.framework.apptoken.impl;
+import com.hys.app.framework.apptoken.*;
+import com.hys.app.framework.apptoken.model.AccessToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.hys.app.framework.util.DateUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * access token管理接接口实现
+ * 对token进行统一的缓存处理
+ * @author kingapex
+ * @version 1.0
+ * @data 2021/8/22 16:33
+ **/
+@Service
+public class AccessTokenManagerImpl implements AccessTokenManager {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+
+    @Autowired
+    private AccessTokenCache accessTokenCache;
+
+    @Autowired
+    AccessTokenGetterFactory accessTokenGetterFactory;
+
+
+
+
+    /**
+     * 从缓存处获取,如果没有由微信获取,并存缓存
+     * @return
+     */
+    @Override
+    public String getToken(AccessTokenIdentifier accessTokenIdentifier) {
+
+
+        //由缓存处获取token
+        AccessToken accessToken = accessTokenCache.get(accessTokenIdentifier);
+
+        Long now = DateUtil.getDateline();
+
+        //如果token已经失效则重新获取token
+        if ( accessToken== null || accessToken.getExpires()<=now ) {
+
+            accessToken = this.getAndCache(accessTokenIdentifier);
+
+            return accessToken.getToken();
+        }
+
+        return accessToken.getToken();
+    }
+
+
+    /**
+     * 强制刷新token并返回
+     * @return
+     */
+    @Override
+    public String refreshToken(AccessTokenIdentifier accessTokenIdentifier) {
+        logger.debug("强制刷新" );
+
+        AccessToken accessToken = this.getAndCache(accessTokenIdentifier);
+
+        return accessToken.getToken();
+    }
+
+    /**
+     * 调用第三方服务api 获取token
+     * @param accessTokenIdentifier token的唯一标识
+     * @return token
+     */
+    private AccessToken getAndCache(AccessTokenIdentifier accessTokenIdentifier) {
+
+        AccessTokenGetter accessTokenGetter = accessTokenGetterFactory.getBean(accessTokenIdentifier.getAppEnum());
+
+        //调用token获取器,由第三个服务api获取token
+        AccessToken  accessToken =accessTokenGetter.getToken(accessTokenIdentifier);
+
+        //存入缓存
+        accessTokenCache.put(accessTokenIdentifier, accessToken);
+
+        return accessToken;
+    }
+
+
+}

+ 50 - 0
framework/src/main/java/com/hys/app/framework/apptoken/model/AccessToken.java

@@ -0,0 +1,50 @@
+package com.hys.app.framework.apptoken.model;
+
+import com.hys.app.framework.util.DateUtil;
+
+import java.io.Serializable;
+
+/**
+ * 第三方应用 accessToken
+ * @author kingapex
+ * @version 1.0
+ * @data 2021/8/22 16:15
+ **/
+public class AccessToken implements Serializable {
+    private static final long serialVersionUID = 9115135330405642L;
+
+    public AccessToken(String token, Long expires) {
+        this.token = token;
+        this.expires = expires;
+    }
+
+    /**
+     * token值
+     */
+    private String token;
+
+    /**
+     * 有效期
+     */
+    private  Long expires;
+
+    public String getToken() {
+        return token;
+    }
+
+    public Long getExpires() {
+        return expires;
+    }
+
+    @Override
+    public String toString() {
+        long now = DateUtil.getDateline();
+        boolean isEnable = now<=expires;
+
+        return "AccessToken{" +
+                "token='" + token + '\'' +
+                ", expires=" + expires +
+                ",isEnable="+isEnable+
+                '}';
+    }
+}

+ 17 - 0
framework/src/main/java/com/hys/app/framework/apptoken/model/AppTypeEnum.java

@@ -0,0 +1,17 @@
+package com.hys.app.framework.apptoken.model;
+
+/**
+ * app 类型 枚举
+ * 获取access token的会根据这个枚举识别token getter
+ * 以便调用相应的第三方应用api。
+ * 如微信,企业微信,支付宝等
+ * @author kingapex
+ * @data 2021/8/29 12:56
+ * @version 1.0
+ **/
+public enum AppTypeEnum {
+
+    WeiXin,
+    QiyeWeiXin
+
+}

+ 17 - 0
framework/src/main/java/com/hys/app/framework/auth/AuthUser.java

@@ -0,0 +1,17 @@
+package com.hys.app.framework.auth;
+
+import java.util.List;
+
+/**
+ * 认证用户接口
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-06-21
+ */
+public interface AuthUser {
+
+    List<String> getRoles();
+
+    void setRoles(List<String> roles);
+}

+ 88 - 0
framework/src/main/java/com/hys/app/framework/auth/BeanUtil.java

@@ -0,0 +1,88 @@
+package com.hys.app.framework.auth;
+
+import org.springframework.beans.BeanUtils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-06-24
+ */
+
+public class BeanUtil {
+
+    public static <T> T mapToBean(Class<T> clz, Map map) {
+
+        T t = BeanUtils.instantiateClass(clz);
+
+        List<Field> fields = getFields(clz);
+        fields.forEach(field -> {
+            String fieldName = field.getName();
+            PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(clz, fieldName);
+            Method setter = propertyDescriptor.getWriteMethod();
+            Object value = map.get(fieldName);
+            try {
+                if (setter != null) {
+                    setter.invoke(t, value);
+                }
+
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (InvocationTargetException e) {
+                e.printStackTrace();
+            }
+        });
+
+        return t;
+    }
+
+    public static List<Field> getFields(Class clz) {
+        List<Field> fields = new ArrayList<>();
+        //获取所有属性(包含父类)
+        fields = getParentField(clz, fields);
+        return fields;
+    }
+
+
+    /**
+     * 递归获取所有父类的属性
+     *
+     * @param calzz
+     * @param list
+     * @return add by liuyulei 2019-02-14
+     */
+    private static List<Field> getParentField(Class<?> calzz, List<Field> list) {
+
+        if (calzz.getSuperclass() != Object.class) {
+            getParentField(calzz.getSuperclass(), list);
+        }
+
+        Field[] fields = calzz.getDeclaredFields();
+        list.addAll(arrayConvertList(fields));
+
+        return list;
+    }
+
+
+    /**
+     * 将数组转换成List
+     *
+     * @param fields
+     * @return add by liuyulei 2019-02-14
+     */
+    private static List<Field> arrayConvertList(Field[] fields) {
+        List<Field> resultList = new ArrayList<>(fields.length);
+        Collections.addAll(resultList, fields);
+        return resultList;
+
+    }
+}

+ 43 - 0
framework/src/main/java/com/hys/app/framework/auth/Token.java

@@ -0,0 +1,43 @@
+package com.hys.app.framework.auth;
+
+/**
+ * Token
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-06-21
+ */
+
+public class Token {
+
+
+    /**
+     * token令牌
+     */
+    private String accessToken;
+
+
+    /**
+     * 刷新token
+     */
+    private String refreshToken;
+
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    public String getRefreshToken() {
+        return refreshToken;
+    }
+
+    public void setRefreshToken(String refreshToken) {
+        this.refreshToken = refreshToken;
+    }
+
+
+}

+ 20 - 0
framework/src/main/java/com/hys/app/framework/auth/TokenCreater.java

@@ -0,0 +1,20 @@
+package com.hys.app.framework.auth;
+
+/**
+ * Token创建接口
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-06-21
+ */
+public interface TokenCreater {
+
+
+    /**
+     * 创建token
+     * @param user 用户
+     * @return token
+     */
+    Token create(AuthUser user);
+
+}

+ 15 - 0
framework/src/main/java/com/hys/app/framework/auth/TokenParseException.java

@@ -0,0 +1,15 @@
+package com.hys.app.framework.auth;
+
+/**
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-06-24
+ */
+
+public class TokenParseException extends RuntimeException {
+
+     public TokenParseException(Throwable cause ) {
+        super(cause);
+    }
+}

+ 20 - 0
framework/src/main/java/com/hys/app/framework/auth/TokenParser.java

@@ -0,0 +1,20 @@
+package com.hys.app.framework.auth;
+
+/**
+ * Token 解析器
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-06-21
+ */
+public interface TokenParser {
+
+    /**
+     * 解析token
+     * @param token
+     * @return 用户对象
+     */
+    <T>  T parse(Class<T> clz, String token) throws TokenParseException;
+
+
+}

+ 33 - 0
framework/src/main/java/com/hys/app/framework/auth/auth.uml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Diagram>
+  <ID>JAVA</ID>
+  <OriginalElement />
+  <nodes>
+    <node x="0.0" y="0.0">com.enation.app.javashop.framework.auth.TokenCreater</node>
+    <node x="0.0" y="324.0">com.enation.app.javashop.framework.auth.Token</node>
+    <node x="262.0" y="113.0">com.enation.app.javashop.framework.auth.impl.JwtTokenParser</node>
+    <node x="267.0" y="0.0">com.enation.app.javashop.framework.auth.TokenParser</node>
+    <node x="216.0" y="324.0">com.enation.app.javashop.framework.auth.AuthUser</node>
+    <node x="0.0" y="113.0">com.enation.app.javashop.framework.auth.impl.JwtTokenCreater</node>
+  </nodes>
+  <notes />
+  <edges>
+    <edge source="com.enation.app.javashop.framework.auth.impl.JwtTokenCreater" target="com.enation.app.javashop.framework.auth.TokenCreater">
+      <point x="0.0" y="-83.0" />
+      <point x="0.0" y="31.5" />
+    </edge>
+    <edge source="com.enation.app.javashop.framework.auth.impl.JwtTokenParser" target="com.enation.app.javashop.framework.auth.TokenParser">
+      <point x="0.0" y="-70.0" />
+      <point x="0.0" y="31.5" />
+    </edge>
+  </edges>
+  <settings layout="Hierarchic Group" zoom="1.0" x="250.0" y="276.0" />
+  <SelectedNodes />
+  <Categories>
+    <Category>Methods</Category>
+    <Category>Properties</Category>
+  </Categories>
+  <SCOPE>All</SCOPE>
+  <VISIBILITY>private</VISIBILITY>
+</Diagram>
+

+ 99 - 0
framework/src/main/java/com/hys/app/framework/auth/impl/JwtTokenCreater.java

@@ -0,0 +1,99 @@
+package com.hys.app.framework.auth.impl;
+
+import com.hys.app.framework.auth.AuthUser;
+import com.hys.app.framework.auth.BeanUtil;
+import com.hys.app.framework.auth.Token;
+import com.hys.app.framework.auth.TokenCreater;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.apache.commons.beanutils.BeanMap;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Jwt token 创建实现
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-06-21
+ */
+
+public class JwtTokenCreater implements TokenCreater {
+
+    private String secret;
+
+    private int accessTokenExp;
+
+    private int refreshTokenExp;
+
+    public JwtTokenCreater(String secret) {
+
+        this.secret = secret;
+
+        accessTokenExp=60*60;
+
+        //默认session失效时间为1小时:60秒 x 60 (=1分钟) * 60 (=1小时)
+        refreshTokenExp = 60 * 60 * 60;
+    }
+
+    @Override
+    public Token create(AuthUser user) {
+
+        ObjectMapper oMapper = new ObjectMapper();
+
+        Map buyerMap = oMapper.convertValue(user, HashMap.class);
+
+        //使用Calendar计算时间的加减,以免出现精度问题
+        Calendar cal = Calendar.getInstance();
+
+        //设置为当期日期
+        cal.setTime(new Date());
+        //由当前日期加上访问令牌的有效时长即为访问令牌失效时间
+        cal.add(Calendar.SECOND,accessTokenExp);
+        String accessToken = Jwts.builder()
+                .setClaims(buyerMap)
+                .setSubject("user")
+                .setExpiration( cal.getTime() )
+                .signWith(SignatureAlgorithm.HS512, secret.getBytes())
+                .compact();
+
+        //设置为当期日期
+        cal.setTime(new Date());
+        //由当前日期加上刷新令牌的有效时长即为刷新令牌失效时间
+        cal.add(Calendar.SECOND,refreshTokenExp);
+        String refreshToken = Jwts.builder()
+                .setClaims(buyerMap)
+                .setSubject("user")
+                .setExpiration( cal.getTime() )
+                .signWith(SignatureAlgorithm.HS512, secret.getBytes())
+                .compact();
+
+        Token token = new Token();
+        token.setAccessToken(accessToken);
+        token.setRefreshToken(refreshToken);
+
+
+        return token;
+    }
+
+
+    public JwtTokenCreater setSecret(String secret) {
+        this.secret = secret;
+        return  this;
+    }
+
+    public JwtTokenCreater setAccessTokenExp(int accessTokenExp) {
+        this.accessTokenExp = accessTokenExp;
+        return  this;
+    }
+
+    public JwtTokenCreater setRefreshTokenExp(int refreshTokenExp) {
+        this.refreshTokenExp = refreshTokenExp;
+        return  this;
+    }
+}

+ 87 - 0
framework/src/main/java/com/hys/app/framework/auth/impl/JwtTokenParser.java

@@ -0,0 +1,87 @@
+package com.hys.app.framework.auth.impl;
+
+import com.hys.app.framework.auth.TokenParseException;
+import com.hys.app.framework.auth.TokenParser;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+
+import java.text.SimpleDateFormat;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * jwt token解析器
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-06-24
+ */
+
+public class JwtTokenParser implements TokenParser {
+
+    private String secret;
+
+    private Claims claims;
+
+    public JwtTokenParser(String secret) {
+        this.secret = secret;
+    }
+
+
+    @Override
+    public <T> T parse(Class<T> clz, String token) throws TokenParseException {
+
+        try {
+            claims
+                    = Jwts.parser()
+                    .setSigningKey(secret.getBytes())
+                    .parseClaimsJws(token).getBody();
+
+            Object obj = claims.get("uid");
+            claims.put("uid", Long.valueOf(obj.toString()));
+
+            Object platformShopId = claims.get("platformShopId");
+            if (Objects.nonNull(platformShopId)) {
+                claims.put("platformShopId", Long.valueOf(platformShopId.toString()));
+            }
+
+            Object clerkId = claims.get("clerkId");
+            if(clerkId != null){
+                claims.put("clerkId", Long.parseLong(clerkId.toString()));
+            }
+            claims.put("context", new HashMap<>());
+
+            T t = cn.hutool.core.bean.BeanUtil.mapToBean(claims, clz, false);
+//            T t = BeanUtil.mapToBean(clz, claims);
+            return t;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new TokenParseException(e);
+        }
+
+    }
+
+    /**
+     * 获取过期时间
+     *
+     * @return
+     */
+    public long getExpiration() {
+        long tokenDate = getFormatDate(claims.getExpiration().toString());
+        return tokenDate;
+    }
+
+
+    private long getFormatDate(String dateFormat) {
+        try {
+            SimpleDateFormat sdf1 = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
+
+            return sdf1.parse(dateFormat).getTime() / 1000;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return 0;
+    }
+}

+ 139 - 0
framework/src/main/java/com/hys/app/framework/cache/Cache.java

@@ -0,0 +1,139 @@
+package com.hys.app.framework.cache;
+
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 缓存接口
+ * @author kingapex
+ *
+ */
+public interface Cache<T> {
+
+	/**
+	 * Get an item from the cache, nontransactionally
+	 * @param key
+	 * @return the cached object or <tt>null</tt>
+	 */
+	T get(Object key);
+
+
+	/**
+	 *  multiGet
+	 * @param keys  要查询的key集合
+	 * @return
+	 */
+	List  multiGet(Collection keys);
+
+	/**
+	 *  使用stringRedisTemplate批量获取
+	 * @param keys  要查询的key集合
+	 * @return
+	 */
+	List  strMultiGet(Collection keys);
+
+	/**
+	 * 批量set
+	 * @param map
+	 */
+	void multiSet(Map map);
+
+
+	/**
+	 * 批量删除
+	 * @param keys 要删除的key集合
+	 */
+	void multiDel(Collection keys);
+
+	/**
+	 * Add an item to the cache, nontransactionally, with
+	 * failfast semantics
+	 * @param key
+	 * @param value
+	 */
+	void put(Object key, T value);
+
+	/**
+	 * 往缓存中写入内容
+	 * @param key
+	 * @param value
+	 * @param exp	超时时间,单位为秒
+	 */
+	void put(Object key, T value, int exp);
+
+	/**
+	 * 删除
+	 * @param key
+	 */
+	void remove(Object key);
+
+	/**
+	 * 删除
+	 * @param key
+	 */
+	void vagueDel(Object key);
+
+	/**
+	 * Clear the cache
+	 */
+	void clear();
+
+
+	/**
+	 * 往缓存中写入内容
+	 * @param key		缓存key
+	 * @param hashKey	缓存中hashKey
+	 * @param hashValue hash值
+	 */
+	void putHash(Object key,Object hashKey,Object hashValue);
+
+	/**
+	 * 玩缓存中写入内容
+	 * @param key
+	 * @param map
+	 */
+	void putAllHash(Object key,Map map);
+
+	/**
+	 * 读取缓存值
+	 * @param key
+	 * @param hashKey
+	 * @return
+	 */
+	T getHash(Object key,Object hashKey);
+
+	/**
+	 * 移除hash中的某个key
+	 * @param key
+	 * @param hashKey
+	 */
+	void removeHash(Object key, Object hashKey);
+
+	/**
+	 * 读取缓存值
+	 * @param key
+	 * @return
+	 */
+	Map<Object,Object> getHash(Object key);
+
+
+	/**
+	 * set类型的操作 写入
+	 * @param key
+	 * @param value
+	 */
+	void setSetValue(Object key, T value);
+
+
+	/**
+	 * set类型的操作 读取
+	 * @param key
+	 * @return
+	 */
+	Set getSetValue(Object key);
+
+
+}

+ 163 - 0
framework/src/main/java/com/hys/app/framework/cache/MybatisRedisCache.java

@@ -0,0 +1,163 @@
+package com.hys.app.framework.cache;
+
+import com.hys.app.framework.context.ApplicationContextHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.redis.connection.RedisServerCommands;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Mybatis二级缓存,使用redis作为缓存。<br/>
+ * 所有的key都是由mybatis生成的,如:<br/>
+ * "1040379671:3603394099:com.hys.app.mapper.UserMapper.selectById:0:2147483647:SELECT id,name,age,email FROM user WHERE id=? :1287644473261592577:MybatisSqlSessionFactoryBean"
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.2.2
+ * 2020/7/27
+ */
+
+public class MybatisRedisCache implements org.apache.ibatis.cache.Cache {
+
+    /**
+     * 读写锁
+     */
+    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
+
+    /**
+     * 操作缓存template
+     */
+    private RedisTemplate<String, Object> redisTemplate;
+
+    /**
+     * 唯一id
+     */
+    private String id;
+
+    private final Logger logger =  LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * 缓存实现构造器
+     *
+     * @param id 唯一id
+     */
+    public MybatisRedisCache(final String id) {
+        if (id == null) {
+            throw new IllegalArgumentException("Cache instances require an ID");
+        }
+        this.id = id;
+    }
+
+    /**
+     * 获取唯一id
+     *
+     * @return
+     */
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    /**
+     * 像缓存中存入一个对象
+     *
+     * @param key
+     * @param value
+     */
+    @Override
+    public void putObject(Object key, Object value) {
+        logger.debug("存入key:"+id+"--"+ key);
+        if (value != null) {
+            getRedisTemplate().opsForHash().put(id,key,value);
+//            getRedisTemplate().opsForValue().set(getKey(key), value);
+        }
+    }
+
+
+    /**
+     * 由缓存中读取对象
+     *
+     * @param key
+     * @return
+     */
+    @Override
+    public Object getObject(Object key) {
+        logger.debug("获取key:"+id+"--"+ key);
+        try {
+            if (key != null) {
+                Object obj =getRedisTemplate().opsForHash().get(id, key);
+                return obj;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 移除一个对象,这个方法只有在发生回滚是调用。
+     * 在删除是不会调用,目的是如果发生了删除且缓存过,则查不用读库
+     * As of 3.3.0 this method is only called during a rollback
+     * for any previous value that was missing in the cache.
+     * This lets any blocking cache to release the lock that
+     * may have previously put on the key.
+     * A blocking cache puts a lock when a value is null
+     * and releases it when the value is back again.
+     * This way other threads will wait for the value to be
+     * available instead of hitting the database.
+     *
+     * @param key
+     * @return
+     */
+    @Override
+    public Object removeObject(Object key) {
+
+        logger.debug("移除key:"+id+"--"+ key);
+
+        if (key != null) {
+            getRedisTemplate().opsForHash().delete(id, key);
+        }
+        return null;
+    }
+
+    @Override
+    public void clear() {
+        logger.debug("移除相关的的key-->" + this.id + "*");
+
+        getRedisTemplate().opsForHash().getOperations().delete(id);
+
+    }
+
+    @Override
+    public int getSize() {
+
+        Long size = getRedisTemplate().execute((RedisCallback<Long>) RedisServerCommands::dbSize);
+        logger.debug("获取大小:"+size.intValue());
+        return size.intValue();
+    }
+
+    /**
+     * 为mybatis提供读写锁
+     *
+     * @return
+     */
+    @Override
+    public ReadWriteLock getReadWriteLock() {
+        return this.readWriteLock;
+    }
+
+
+    private RedisTemplate<String, Object> getRedisTemplate() {
+        if (redisTemplate == null) {
+            redisTemplate = (RedisTemplate<String, Object>) ApplicationContextHolder.getBean("redisTemplate");
+        }
+        return redisTemplate;
+    }
+
+
+}
+

+ 137 - 0
framework/src/main/java/com/hys/app/framework/cache/RedisCacheImpl.java

@@ -0,0 +1,137 @@
+package com.hys.app.framework.cache;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * redis的cache实现
+ *
+ * @author fk
+ * @version v6.4
+ * @since v6.4 2017年9月25日 下午4:32:49
+ */
+@Component
+public class RedisCacheImpl implements Cache {
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+
+    public RedisCacheImpl() {
+
+    }
+
+    @Override
+    public Object get(Object key) {
+
+        return redisTemplate.opsForValue().get(key);
+    }
+
+    @Override
+    public List multiGet(Collection  keys) {
+       return redisTemplate.opsForValue().multiGet(keys);
+
+    }
+
+    @Override
+    public List strMultiGet(Collection keys) {
+
+        return stringRedisTemplate.opsForValue().multiGet(keys);
+    }
+
+
+    @Override
+    public void multiSet(Map map) {
+        redisTemplate.opsForValue().multiSet(map);
+    }
+
+    @Override
+    public void multiDel(Collection keys) {
+        redisTemplate.delete(keys);
+    }
+
+    @Override
+    public void put(Object key, Object value) {
+
+        redisTemplate.opsForValue().set(key, value);
+    }
+
+    @Override
+    public void put(Object key, Object value, int exp) {
+
+        redisTemplate.opsForValue().set(key, value, exp, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public void remove(Object key) {
+
+        redisTemplate.delete(key);
+    }
+
+    /**
+     * 删除
+     *
+     * @param key
+     */
+    @Override
+    public void vagueDel(Object key) {
+        Set<String> keys = redisTemplate.keys(key + "*");
+        redisTemplate.delete(keys);
+    }
+
+    @Override
+    public void clear() {
+
+        Set keys = redisTemplate.keys("*");
+        redisTemplate.delete(keys);
+    }
+
+    @Override
+    public void putHash(Object key, Object hashKey, Object hashValue) {
+        redisTemplate.opsForHash().put(key, hashKey, hashValue);
+    }
+
+    @Override
+    public void putAllHash(Object key, Map map) {
+        redisTemplate.opsForHash().putAll(key, map);
+    }
+
+    @Override
+    public Object getHash(Object key, Object hashKey) {
+        return redisTemplate.opsForHash().get(key, hashKey);
+    }
+
+    @Override
+    public void removeHash(Object key, Object hashKey) {
+        redisTemplate.opsForHash().delete(key, hashKey);
+    }
+
+    @Override
+    public Map<Object, Object> getHash(Object key) {
+        return this.redisTemplate.opsForHash().entries(key);
+    }
+
+
+    @Override
+    public void setSetValue(Object key, Object value) {
+        redisTemplate.boundSetOps(key).add(value);
+    }
+
+    @Override
+    public Set getSetValue(Object key) {
+        Set set = redisTemplate.boundSetOps(key).members();
+        return set;
+    }
+
+}

+ 68 - 0
framework/src/main/java/com/hys/app/framework/context/ApplicationContextHolder.java

@@ -0,0 +1,68 @@
+package com.hys.app.framework.context;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * spring 上下文件获取类
+ * Created by kingapex on 2018/3/23.
+ * @author kingapex
+ * @version 1.0
+ * @since 7.0.0
+ * 2018/3/23
+ */
+@Component
+public class ApplicationContextHolder implements ApplicationContextAware {
+
+    /**
+     * 上下文对象实例
+     */
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void setApplicationContext(ApplicationContext appContext) throws BeansException {
+        applicationContext = appContext;
+    }
+
+    /**
+     * 获取applicationContext
+     * @return  spring applicationContext
+     */
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    /**
+     * 通过name获取 Bean.
+     * @param name bean的名字
+     * @return bean实例
+     */
+    public static Object getBean(String name){
+        return getApplicationContext().getBean(name);
+    }
+
+    /**
+     * 通过class获取Bean.
+     * @param clazz bean的类型
+     * @param <T> bean的类型
+     * @return bean实例
+     */
+    public static <T> T getBean(Class<T> clazz){
+        return getApplicationContext().getBean(clazz);
+    }
+
+    /**
+     * 通过name,以及Clazz返回指定的Bean
+     * @param name bean的名字
+     * @param clazz bean的类型
+     * @param <T>  bean的类型
+     * @return bean实例
+     */
+    public static <T> T getBean(String name,Class<T> clazz){
+        return getApplicationContext().getBean(name, clazz);
+    }
+
+}

+ 117 - 0
framework/src/main/java/com/hys/app/framework/context/WebInterceptorConfigurer.java

@@ -0,0 +1,117 @@
+package com.hys.app.framework.context;
+
+import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
+import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
+import com.hys.app.framework.context.params.SnakeToCamelArgumentResolver;
+import com.hys.app.framework.context.params.SnakeToCamelModelAttributeMethodProcessor;
+import com.hys.app.framework.context.request.HysRequestInterceptor;
+import com.hys.app.framework.context.user.SnakeCaseInterceptor;
+import com.hys.app.framework.parameter.ParameterFilter;
+import com.hys.app.framework.security.XssStringJsonSerializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import javax.servlet.DispatcherType;
+import java.util.List;
+
+/**
+ * 加载自定义的 拦截器
+ *
+ * @author Chopper
+ * @version v1.0
+ * @since v6.2 2017年3月7日 下午5:29:56
+ */
+@Configuration
+public class WebInterceptorConfigurer implements WebMvcConfigurer {
+
+    @Autowired
+    private SnakeCaseInterceptor snakeCaseInterceptor;
+
+    @Override
+    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
+
+        //参数蛇形转驼峰拦截
+        argumentResolvers.add(new SnakeToCamelModelAttributeMethodProcessor(true));
+        argumentResolvers.add(new SnakeToCamelArgumentResolver());
+
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(hysRequestInterceptor()).addPathPatterns("/**");
+        registry.addInterceptor(snakeCaseInterceptor).addPathPatterns("/admin/erp/kdn/returnBack");
+
+    }
+
+    /**
+     * 声明hysRequestInterceptor bean
+     *
+     * @return
+     */
+    @Bean
+    public HysRequestInterceptor hysRequestInterceptor() {
+        return new HysRequestInterceptor();
+    }
+
+
+    /**
+     * 自定义 ObjectMapper ,用于xss攻击过滤
+     *
+     * @param builder
+     * @return
+     */
+    @Bean
+    @Primary
+    public ObjectMapper xssObjectMapper(Jackson2ObjectMapperBuilder builder) {
+        //解析器
+        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
+        //注册xss解析器
+        SimpleModule xssModule = new SimpleModule("XssStringJsonSerializer");
+        xssModule.addSerializer(new XssStringJsonSerializer());
+        objectMapper.registerModule(xssModule);
+        //返回
+        return objectMapper;
+    }
+
+
+    /**
+     * 处理参数拦截器声明,目前只处理emoji表情拦截
+     *
+     * @return
+     */
+    @Bean
+    public FilterRegistrationBean<ParameterFilter> parameterFilter() {
+        FilterRegistrationBean<ParameterFilter> filter = new FilterRegistrationBean<>();
+        filter.setDispatcherTypes(DispatcherType.REQUEST);
+        filter.setFilter(new ParameterFilter());
+        //拦截所有请求,如果没有设置则默认“/*”
+        filter.addUrlPatterns("/*");
+        //设置注册的名称,如果没有指定会使用Bean的名称。此name也是过滤器的名称
+        filter.setName("parameterFilter");
+        //该filter在filterChain中的执行顺序
+        filter.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
+        return filter;
+    }
+
+
+    /**
+     * 雪花算法生成唯一数字的bean
+     *
+     * @return
+     */
+    @Bean
+    public IdentifierGenerator keyGenerator() {
+        return new DefaultIdentifierGenerator();
+    }
+
+
+}

+ 25 - 0
framework/src/main/java/com/hys/app/framework/context/app/AppTypeContext.java

@@ -0,0 +1,25 @@
+package com.hys.app.framework.context.app;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * APP类型上下文
+ * @author 张崧
+ * @version 1.0
+ * @since 7.2.2
+ * 2021-08-26
+ */
+public class AppTypeContext {
+
+    public static AppTypeEnum get(){
+        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+//        String appTypeStr =  ThreadContextHolder.getHttpRequest().getHeader("AppType");
+        String appTypeStr =  httpServletRequest.getHeader("AppType");
+        return AppTypeEnum.valueOf(appTypeStr);
+    }
+
+
+}

+ 33 - 0
framework/src/main/java/com/hys/app/framework/context/app/AppTypeEnum.java

@@ -0,0 +1,33 @@
+package com.hys.app.framework.context.app;
+
+/**
+ * AppType枚举
+ * @author 张崧
+ * @version 1.0
+ * @since 7.2.2
+ * 2021-08-26
+ */
+public enum AppTypeEnum {
+
+    Buyer("买家端"),
+
+    Admin("平台管理端"),
+
+    Shop("门店管理端"),
+
+    Cashier("收银台"),
+
+    Guide("智慧导购");
+
+    private String description;
+
+    AppTypeEnum(String description) {
+        this.description = description;
+
+    }
+
+    public String description() {
+        return description;
+    }
+
+}

+ 108 - 0
framework/src/main/java/com/hys/app/framework/context/instance/AppInstance.java

@@ -0,0 +1,108 @@
+package com.hys.app.framework.context.instance;
+
+import java.io.Serializable;
+import java.util.UUID;
+
+/**
+ * App 实例
+ * @author kingapex
+ * @version 1.0
+ * @since 7.2.1
+ * 2020/4/14
+ */
+
+public class AppInstance implements Serializable {
+
+    private static final long serialVersionUID = 4597928439838933735L;
+
+    /**
+     * 全局唯一实例
+     */
+    private static AppInstance instance;
+
+    /**
+     * 单例的工厂方法
+     * @return
+     */
+    public static AppInstance getInstance() {
+        if (instance == null) {
+            instance = new AppInstance();
+            instance.setUuid(UUID.randomUUID().toString());
+        }
+        return instance;
+    }
+
+    /**
+     * 保护起来,只允许工厂实例化
+     */
+    private AppInstance() {
+
+    }
+
+
+    /**
+     * work id 是唯一的一个int值,如1,2,3
+     * 不仅是在app 多实例内唯一,是全局唯一的
+     */
+    private int workId;
+
+    /**
+     * 实例标识自己用的,比如注册到中心时标识自己
+     * 以便在中心中确定是自己注册的
+     */
+    private String uuid;
+
+
+    /**
+     * app的名字,如base-api buyer-api,用来显示的
+     */
+    private String appName;
+
+    /**
+     * instance 的有效期,如果无效,应该被剔除
+     */
+    private Long expirationTime;
+
+
+    public int getWorkId() {
+        return workId;
+    }
+
+    public void setWorkId(int workId) {
+        this.workId = workId;
+    }
+
+    public String getUuid() {
+        return uuid;
+    }
+
+    protected void setUuid(String uuid) {
+        this.uuid = uuid;
+    }
+
+    public String getAppName() {
+        return appName;
+    }
+
+    public void setAppName(String appName) {
+        this.appName = appName;
+    }
+
+    public Long getExpirationTime() {
+        return expirationTime;
+    }
+
+    public void setExpirationTime(Long expirationTime) {
+        this.expirationTime = expirationTime;
+    }
+
+    @Override
+    public String toString() {
+        return "AppInstance{" +
+                "workId=" + workId +
+                ", uuid='" + uuid + '\'' +
+                ", appName='" + appName + '\'' +
+                ", expirationTime=" + expirationTime +
+                '}';
+    }
+}

+ 13 - 0
framework/src/main/java/com/hys/app/framework/context/instance/HeartBeatProxy.java

@@ -0,0 +1,13 @@
+package com.hys.app.framework.context.instance;
+
+/**
+ * 心跳检测代理类
+ */
+public interface HeartBeatProxy {
+
+    /**
+     * 服务心跳检测
+     */
+    void heartbeat();
+
+}

+ 33 - 0
framework/src/main/java/com/hys/app/framework/context/instance/InstanceContext.java

@@ -0,0 +1,33 @@
+package com.hys.app.framework.context.instance;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2020/4/15
+ */
+
+public interface InstanceContext {
+
+    /**
+     * 读取app列表
+     * @return
+     */
+    Set<String> getApps();
+
+    /**
+     * 获取实例列表
+     * @return
+     */
+    List<AppInstance> getInstances();
+
+    /**
+     * 注册本实例
+     */
+    void register();
+
+
+}

+ 186 - 0
framework/src/main/java/com/hys/app/framework/context/instance/InstanceContextImpl.java

@@ -0,0 +1,186 @@
+package com.hys.app.framework.context.instance;
+
+import com.hys.app.framework.cache.Cache;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.hys.app.framework.util.DateUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * 实例上下文基于redis的实现
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2020/4/15
+ */
+@Service
+public class InstanceContextImpl implements InstanceContext,HeartBeatProxy {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    @Lazy
+    private Cache redisCache;
+
+    /**
+     * 实例缓存key
+     */
+    public static String REDIS_KEY = "{instances}";
+
+    /**
+     * app name,由spring.application.name配置获取
+     */
+    @Value("${spring.application.name}")
+    private String appName;
+
+    @Override
+    public Set<String> getApps() {
+        List<AppInstance> instances = getInstances();
+        Set<String> list = new HashSet();
+        for (AppInstance instance : instances) {
+            list.add(instance.getAppName());
+        }
+        return list;
+    }
+
+
+    @Override
+    public List<AppInstance> getInstances() {
+        List<AppInstance> instances = (List) redisCache.get(REDIS_KEY);
+
+        if (instances == null) {
+            instances = new ArrayList<>();
+        }
+
+        return instances;
+    }
+
+    @Override
+    public void register() {
+
+        List<AppInstance> instances = getInstances();
+
+        List<Integer> workIdList = new ArrayList<>();
+
+        logger.debug("现有实例:");
+        for (AppInstance instance : instances) {
+            logger.debug(String.valueOf(instance.getWorkId()));
+            workIdList.add(instance.getWorkId());
+        }
+
+        //按大小排好序
+        Collections.sort(workIdList);
+
+        //用于记录上一个id
+        int preId = 0;
+        int newId = 0;
+
+        for (Integer workId : workIdList) {
+
+            //workId不连续,使workid补充上
+            if (workId - 1 != preId) {
+                newId = preId + 1;
+                break;
+            }
+            preId = workId;
+        }
+
+        //如果newId为0,说明workid都是连续的
+        if (newId == 0) {
+            newId = workIdList.size() + 1;
+        }
+
+        //将本实例压入list
+        AppInstance instance = AppInstance.getInstance();
+        logger.debug("此实例workId:"+newId);
+        instance.setWorkId(newId);
+
+        Long now = DateUtil.getDateline();
+        Long expirationTime = now + 30;
+        instance.setExpirationTime(expirationTime);
+
+        logger.debug("实例名称:"+appName);
+        instance.setAppName(appName);
+
+        instances.add(instance);
+
+        //更新缓存
+        redisCache.put(REDIS_KEY, instances);
+
+    }
+
+
+    /**
+     * 服务心跳
+     * 每个10秒执行
+     */
+    @Async
+    @Scheduled(fixedRate = 10000)
+    @Override
+    public void heartbeat() {
+        try {
+            checkHangUp();
+        } catch (Exception e) {
+            logger.error("服务心跳异常:",e);
+        }
+    }
+
+    /**
+     * 检测挂机实例,移除
+     */
+    private void checkHangUp() {
+        List<AppInstance> instances = getInstances();
+        if (instances == null || instances.isEmpty()) {
+            return;
+        }
+        //单例
+        AppInstance instance = AppInstance.getInstance();
+
+        Long now = DateUtil.getDateline();
+        Long expirationTime = now + 30;
+        //挂机的实例列表
+        List<AppInstance> shutdownList = new ArrayList<>();
+        //默认本实例不存在
+        boolean instanceExist = false;
+        for (AppInstance appInstance : instances) {
+
+            //找到当前的instance,并延期30秒
+            if (instance.getUuid().equals(appInstance.getUuid())) {
+                appInstance.setExpirationTime(expirationTime);
+                instanceExist = true;
+            }
+
+
+            //过期判定为超期,已经down掉了
+            if (appInstance.getExpirationTime() < now) {
+                shutdownList.add(appInstance);
+                logger.debug("挂机实例,appName:{},workId:{},uuid:{}",appInstance.getAppName(),appInstance.getWorkId(),appInstance.getUuid());
+            }
+
+        }
+
+        //移除掉down掉的instance
+        for (AppInstance appInstance : shutdownList) {
+            instances.remove(appInstance);
+        }
+
+        //更新缓存
+        redisCache.put(REDIS_KEY, instances);
+
+        //判断本实例是否存在
+        if (!instanceExist) {
+            logger.debug("服务实例{}不存在,将实例本身{}塞入缓存",appName,instance.getWorkId());
+            //如果不存在,则将本实例注册进去
+            register();
+        }
+    }
+
+
+}

+ 28 - 0
framework/src/main/java/com/hys/app/framework/context/instance/ScheduledConfig.java

@@ -0,0 +1,28 @@
+package com.hys.app.framework.context.instance;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.SchedulingConfigurer;
+import org.springframework.scheduling.config.ScheduledTaskRegistrar;
+import org.springframework.stereotype.Service;
+
+/**
+ * 定时任务线程池配置
+ * @author liuyulei
+ * @version 1.0
+ * @since 7.2.2
+ * 2020/11/18 0018  14:30
+ */
+@Service
+public class ScheduledConfig implements SchedulingConfigurer {
+
+    @Autowired
+    private AsyncConfigurer asyncConfigurer;
+
+    @Override
+    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
+        scheduledTaskRegistrar.setScheduler(asyncConfigurer.getAsyncExecutor());
+//        scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
+
+    }
+}

+ 51 - 0
framework/src/main/java/com/hys/app/framework/context/params/JacksonConfig.java

@@ -0,0 +1,51 @@
+package com.hys.app.framework.context.params;
+
+import com.hys.app.framework.database.WebPage;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+
+import java.io.IOException;
+
+/**
+ * 自定义json格式转换
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.2.1
+ * 2020/6/14
+ */
+@Configuration
+public class JacksonConfig {
+
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer customJackson() {
+        return new Jackson2ObjectMapperBuilderCustomizer() {
+            @Override
+            public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
+
+                //定义Long型的Json 值转换,转换为String型
+                //为了适配javascript 不支持Long型的精度
+                jacksonObjectMapperBuilder.serializerByType(Long.class, new JsonSerializer() {
+                    @Override
+                    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+                        //如果是Page对象则不转换为String型,而是返回Long类型(为了防止前端分页功能报错)
+                        Object currentValue = gen.getCurrentValue();
+                        if (currentValue != null && WebPage.class.equals(currentValue.getClass())) {
+                            gen.writeNumber(String.valueOf(value));
+                        } else {
+                            gen.writeString(String.valueOf(value));
+                        }
+
+                    }
+                });
+            }
+
+        };
+    }
+
+}

+ 89 - 0
framework/src/main/java/com/hys/app/framework/context/params/SnakeToCamelArgumentResolver.java

@@ -0,0 +1,89 @@
+package com.hys.app.framework.context.params;
+
+import com.hys.app.framework.util.XssUtil;
+import com.google.common.base.CaseFormat;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.SimpleTypeConverter;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+/**
+ * 蛇形转驼峰参数转换器
+ * 以及防xss的参数过滤
+ * 用于基本类型
+ * @author kingapex
+ * @version 1.0
+ * @since 7.0.0
+ * 2017年5月19日上午12:45:33
+ */
+public class SnakeToCamelArgumentResolver implements HandlerMethodArgumentResolver {
+
+	/**
+	 * 默认分页大小
+	 */
+	private static final long DEFAULT_SIZE = 10;
+
+	/**
+	 * 默认开始页码
+	 */
+	private static final long DEFAULT_NO = 1;
+
+	/**
+	 * 定义只有基本类型参数才转换
+	 * @param parameter spring机制传过来的当前要处理的参数
+	 * @return 只有基本类型参数才返回true,否则返回false
+	 */
+	@Override
+	public boolean supportsParameter(MethodParameter parameter) {
+		//只处理基本类型的参数
+		return  BeanUtils.isSimpleProperty(parameter.getParameterType());
+	}
+
+
+	/**
+	 * 将蛇形参数值转换给驼峰参数值
+	 * @param parameter 要处理的参数
+	 * @param mavContainer spring mavContainer
+	 * @param webRequest  spring  webRequest
+	 * @param binderFactory spring binderFactory
+	 * @return 转换后的参数值
+	 * @throws Exception
+	 */
+	@Override
+	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
+                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+
+		//将蛇形参数名转为驼峰参数名,再读值返回
+		String paramName  = parameter.getParameterName();
+		String camelName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, paramName);
+		SimpleTypeConverter converter = new SimpleTypeConverter();
+
+		Object param = converter.convertIfNecessary(webRequest.getParameter(camelName),parameter.getParameterType());
+
+		//如果是字串型参数,则进行xss过滤
+		if( param instanceof String){
+			param = XssUtil.clean((String)param);
+		}
+
+		//分页相关,增加默认分页
+		if(param == null ){
+			if ("pageNo".equals(paramName)) {
+
+				return DEFAULT_NO;
+			}
+
+			if ("pageSize".equals(paramName)) {
+
+				return DEFAULT_SIZE;
+			}
+		}
+
+		return 	param;
+
+	}
+
+
+}

+ 56 - 0
framework/src/main/java/com/hys/app/framework/context/params/SnakeToCamelModelAttributeMethodProcessor.java

@@ -0,0 +1,56 @@
+package com.hys.app.framework.context.params;
+
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
+
+import javax.servlet.ServletRequest;
+
+/**
+ * 蛇形转驼峰参数转换器
+ * 用于非基本类型
+ * Created by kingapex on 2018/3/20.
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.0.0
+ * 2018/3/20
+ */
+public class SnakeToCamelModelAttributeMethodProcessor extends ServletModelAttributeMethodProcessor {
+
+
+    /**
+     * 构造函数
+     *
+     * @param annotationNotRequired 注解是否必须
+     */
+    public SnakeToCamelModelAttributeMethodProcessor(boolean annotationNotRequired) {
+        super(annotationNotRequired);
+    }
+
+    /**
+     * 绑定蛇形转驼峰Binder
+     *
+     * @param binder  spring 机制传琛来的binder
+     * @param request spring机制的web request
+     */
+    @Override
+    @InitBinder
+    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
+        SnakeToCamelRequestDataBinder camelBinder = new SnakeToCamelRequestDataBinder(binder.getTarget(), binder.getObjectName());
+        ServletRequest req= request.getNativeRequest(ServletRequest.class);
+//        Enumeration<String> paramNames = req.getParameterNames();
+//
+//        while (paramNames.hasMoreElements()){
+//            String name = paramNames.nextElement();
+//            if (name.contains("[")||name.contains("]")) {
+//                return;
+//            }
+//        }
+        camelBinder.bind(req);
+        super.bindRequestParameters(binder, request);
+    }
+
+
+}

+ 73 - 0
framework/src/main/java/com/hys/app/framework/context/params/SnakeToCamelRequestDataBinder.java

@@ -0,0 +1,73 @@
+package com.hys.app.framework.context.params;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.CaseFormat;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValue;
+import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder;
+
+import javax.servlet.ServletRequest;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 蛇形转驼峰数据绑定器
+ * Created by kingapex on 2018/3/20.
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.0.0
+ * 2018/3/20
+ */
+public class SnakeToCamelRequestDataBinder extends ExtendedServletRequestDataBinder {
+
+
+    /**
+     * 构造器,根据spring机制机要求存在
+     *
+     * @param target     spring 机制传递 target
+     * @param objectName spring 机制传递 objectName
+     */
+    public SnakeToCamelRequestDataBinder(Object target, String objectName) {
+        super(target, objectName);
+    }
+
+    /**
+     * 对蛇形参数绑定值
+     *
+     * @param mpvs    spring 机制传递   mpvs
+     * @param request spring 机制传递  request
+     */
+    @Override
+    protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
+        super.addBindValues(mpvs, request);
+
+        //处理JsonProperty注释的对象
+        Class<?> targetClass = getTarget().getClass();
+        Field[] fields = targetClass.getDeclaredFields();
+        for (Field field : fields) {
+            JsonProperty jsonPropertyAnnotation = field.getAnnotation(JsonProperty.class);
+            if (jsonPropertyAnnotation != null && mpvs.contains(jsonPropertyAnnotation.value())) {
+                if (!mpvs.contains(field.getName())) {
+                    mpvs.add(field.getName(), mpvs.getPropertyValue(jsonPropertyAnnotation.value()).getValue());
+                }
+            }
+        }
+
+        List<PropertyValue> covertValues = new ArrayList<PropertyValue>();
+        for (PropertyValue propertyValue : mpvs.getPropertyValueList()) {
+            if (propertyValue.getName().contains("_")) {
+                String camelName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, propertyValue.getName());
+                if (!mpvs.contains(camelName)) {
+                    covertValues.add(new PropertyValue(camelName, propertyValue.getValue()));
+                }
+            }
+        }
+
+
+        mpvs.getPropertyValueList().addAll(covertValues);
+    }
+
+
+}

+ 152 - 0
framework/src/main/java/com/hys/app/framework/context/request/HysRequestInterceptor.java

@@ -0,0 +1,152 @@
+package com.hys.app.framework.context.request;
+
+import com.hys.app.framework.ErpConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.hys.app.framework.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.lang.Nullable;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.net.MalformedURLException;
+
+
+/**
+ * 上下文初始化
+ * 以及跨域的支持
+ *
+ * @author kingapex
+ * @version v1.0
+ * @since v7.0.0
+ * 2018年3月23日 上午10:26:41
+ */
+public class HysRequestInterceptor extends HandlerInterceptorAdapter {
+
+    @Autowired(required = false)
+    private ErpConfig erpConfig;
+
+    @Value("${spring.cloud.config.profile:dev}")
+    private String profile;
+
+    private AntPathMatcher matcher = new AntPathMatcher();
+
+    private static Logger logger = LoggerFactory.getLogger(HysRequestInterceptor.class);
+
+    /**
+     * 拦截request和response并放到上下文中
+     *
+     * @param request  要拦截的request
+     * @param response 要拦截的response
+     * @param handler  spring 机制传递过来的
+     * @return 不中断,继续执行,返回为true
+     * @throws Exception
+     */
+    @Override
+    public boolean preHandle(HttpServletRequest request,
+                             HttpServletResponse response, Object handler) throws Exception {
+
+        boolean flag = checkReferer(request, response);
+
+        if (flag) {
+            //request 和response存到 上下文中
+            ThreadContextHolder.setHttpResponse(response);
+            ThreadContextHolder.setHttpRequest(request);
+            String  shopHeader = request.getHeader("Chain-Shop");
+            if(!StringUtil.isEmpty(shopHeader)){
+                //解码
+                String shopInfo = java.net.URLDecoder.decode(shopHeader,"UTF-8");
+                String[] split = shopInfo.split(",");
+                String shopId  =split[0];
+                String shopName = split[1];
+                Shop shop = new Shop(Long.parseLong(shopId), shopName);
+                ShopContextHolder.setShop(shop);
+            }
+
+            return super.preHandle(request, response, handler);
+        }
+
+        return false;
+
+    }
+
+
+
+    @Value("${erp.referer.checked:false}")
+    private boolean checked;
+
+    /**
+     * 验证referer
+     *
+     * @param request
+     * @param response
+     * @return
+     */
+    private boolean checkReferer(HttpServletRequest request,
+                                 HttpServletResponse response) {
+        if (!checked) {
+            return true;
+        }
+        String referer = request.getHeader("referer");
+        String host = request.getServerName();
+        String uri = request.getRequestURI();
+
+        logger.debug("referer=" + referer);
+        logger.debug("uri=" + request.getRequestURI());
+
+        boolean flag = uri.startsWith("/client")
+                || "/load-customwords".equals(uri)
+                || "/swagger-ui.html".equals(uri)
+                || uri.startsWith("/order/pay/weixin/qr")
+                || uri.startsWith("/order/pay/weixin/status")
+                || uri.startsWith("/payment/callback")
+                || uri.startsWith("/payment/return")
+                || uri.startsWith("/debugger")
+                || uri.contains("/callback")
+                || uri.startsWith("/passport/connect");
+        if (referer == null && flag) {
+            // 状态置为404
+            return true;
+        } else if (referer == null) {
+            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+            logger.debug("本次请求的referer为空");
+            return false;
+        }
+
+        java.net.URL url = null;
+        try {
+            url = new java.net.URL(referer);
+        } catch (MalformedURLException e) {
+            // URL解析异常,也置为404
+            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+            return false;
+        }
+        // 首先判断请求域名和referer域名是否相同
+        if (!host.equals(url.getHost())) {
+            // 如果不等,判断是否在白名单中
+            logger.debug("当前referer没有加入到配置中:" + url.getHost());
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 处理完成 从上下文中移除 request 和respseon
+     *
+     * @param request
+     * @param response
+     * @param handler
+     * @param ex
+     * @throws Exception
+     */
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
+        ThreadContextHolder.remove();
+
+        super.afterCompletion(request, response, handler, ex);
+    }
+}

+ 48 - 0
framework/src/main/java/com/hys/app/framework/context/request/Shop.java

@@ -0,0 +1,48 @@
+package com.hys.app.framework.context.request;
+
+
+/**
+ * 门店
+ *
+ * @author 张崧
+ * @version v7.0
+ * @date 2021-08-26
+ * @since v7.0
+ */
+
+public class Shop {
+
+    /**
+     * 门店id
+     */
+    private Long shopId;
+    /**
+     * 门店名称
+     */
+    private String shopName;
+
+    public Long getShopId() {
+        return shopId;
+    }
+
+    public void setShopId(Long shopId) {
+        this.shopId = shopId;
+    }
+
+    public String getShopName() {
+        return shopName;
+    }
+
+    public void setShopName(String shopName) {
+        this.shopName = shopName;
+    }
+
+    public Shop() {
+        
+    }
+
+    public Shop(Long shopId, String shopName) {
+        this.setShopId(shopId);
+        this.setShopName(shopName);
+    }
+}

+ 26 - 0
framework/src/main/java/com/hys/app/framework/context/request/ShopContextHolder.java

@@ -0,0 +1,26 @@
+package com.hys.app.framework.context.request;
+
+
+
+/**
+ *  用ThreadLocal来存储Session,以便实现Session any where
+ * @author kingapex
+ * <p>2009-12-17 下午03:10:09</p>
+ * @version 1.1
+ * 新增request any where
+ */
+public class ShopContextHolder {
+
+
+	private static ThreadLocal<Shop> shopThreadLocalHolder  = new ThreadLocal<Shop>();
+
+
+	public static void setShop(Shop shop){
+
+		shopThreadLocalHolder.set(shop);
+	}
+
+	public static Shop getShop(){
+		return  shopThreadLocalHolder.get();
+	}
+}

+ 49 - 0
framework/src/main/java/com/hys/app/framework/context/request/ThreadContextHolder.java

@@ -0,0 +1,49 @@
+package com.hys.app.framework.context.request;
+
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+
+/**
+ *  用ThreadLocal来存储Session,以便实现Session any where
+ * @author kingapex
+ * <p>2009-12-17 下午03:10:09</p>
+ * @version 1.1
+ * 新增request any where
+ */
+public class ThreadContextHolder  {
+
+	private static ThreadLocal<HttpServletRequest> requestThreadLocalHolder = new ThreadLocal<HttpServletRequest>();
+	private static ThreadLocal<HttpServletResponse> responseThreadLocalHolder = new ThreadLocal<HttpServletResponse>();
+
+
+	public static void setHttpRequest(HttpServletRequest request){
+
+		requestThreadLocalHolder.set(request);
+	}
+
+	public static void setHttpResponse(HttpServletResponse response){
+		responseThreadLocalHolder.set(response);
+	}
+
+
+	public static void remove(){
+		requestThreadLocalHolder.remove();
+		responseThreadLocalHolder.remove();
+	}
+
+
+
+	public static HttpServletResponse getHttpResponse(){
+
+		return responseThreadLocalHolder.get();
+	}
+	public static HttpServletRequest getHttpRequest(){
+		return  requestThreadLocalHolder.get();
+	}
+
+
+
+}

+ 151 - 0
framework/src/main/java/com/hys/app/framework/context/user/AdminUserContext.java

@@ -0,0 +1,151 @@
+package com.hys.app.framework.context.user;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.hys.app.framework.exception.ServiceException;
+import com.hys.app.framework.security.model.Admin;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ * 用户上下文
+ * Created by kingapex on 2018/3/12.
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.0.0
+ * 2018/3/12
+ */
+public class AdminUserContext {
+
+    /**
+     * 获取当前管理员
+     *
+     * @return
+     */
+    public static Admin getAdmin() {
+
+
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication == null) {
+            return null;
+        }
+        Object someOne = authentication.getDetails();
+        if (someOne != null && someOne instanceof Admin) {
+            return (Admin) someOne;
+        }
+        return null;
+
+
+    }
+
+    /**
+     * 获取当前管理员id
+     *
+     * @return
+     */
+    public static Long getAdminUserId() {
+        Admin admin = getAdmin();
+        if (admin == null) {
+            return null;
+        }
+        return admin.getUid();
+    }
+
+    /**
+     * 获取当前管理员账号
+     *
+     * @return
+     */
+    public static String getAdminUserName() {
+        Admin admin = getAdmin();
+        if (admin == null) {
+            return null;
+        }
+        return admin.getUsername();
+    }
+
+    /**
+     * 获取当前管理员账号
+     *
+     * @return
+     */
+    public static Long getCompanyId() {
+        Admin admin = getAdmin();
+        if (admin == null) {
+            return null;
+        }
+        if (ObjectUtil.isNull(admin.getCompanyId())) {
+            return null;
+        }
+        return admin.getCompanyId();
+    }
+
+    /**
+     * 获取当前管理员账号
+     *
+     * @return
+     */
+    public static Long hasCompany() {
+        Admin admin = getAdmin();
+        if (admin == null) {
+            throw new ServiceException("未查询到人员信息");
+        }
+        if (ObjectUtil.isNull(admin.getCompanyId()) || admin.getFounder() == 1) {
+            throw new ServiceException("平台工作人员不允许操作公司内部业务");
+        }
+        return admin.getCompanyId();
+    }
+
+    /**
+     * 获取当前管理员
+     *
+     * @return
+     */
+    public static Boolean hasFound() {
+        Admin admin = getAdmin();
+        if (admin != null && admin.getFounder() == 1) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 获取当前管理员
+     *
+     * @return
+     */
+    public static void hasAdmin() {
+        if (AdminUserContext.hasFound()) {
+            throw new ServiceException("平台工作人员,不允许进行此操作");
+        }
+        if (!"admin".equals(AdminUserContext.getAdminUserName())) {
+            throw new ServiceException("只有管理员才能进行此操作");
+        }
+    }
+
+    /**
+     * 获取当前管理员
+     *
+     * @return
+     */
+    public static void hasAdminAndSuper() {
+        if (!AdminUserContext.hasFound() && !"admin".equals(AdminUserContext.getAdminUserName())) {
+            throw new ServiceException("只有管理员才能进行此操作");
+        }
+    }
+
+
+    /**
+     * 是否是平台管理员登录    todo 后面做到平台管理员选择公司的时候  在那时候放入公司id
+     *
+     * @return
+     */
+    public static void hasSuperAdminQuery(Long companyId) {
+        Admin admin = getAdmin();
+        if (admin.getFounder() == 1 && ObjectUtil.isNull(companyId)) {
+            throw new ServiceException("平台管理员查看业务数据请先选择公司");
+        }else if (admin.getFounder() == 1 && ObjectUtil.isNotNull(companyId)){
+            admin.setCompanyId(companyId);
+        }
+    }
+}

+ 33 - 0
framework/src/main/java/com/hys/app/framework/context/user/ChainUserContext.java

@@ -0,0 +1,33 @@
+package com.hys.app.framework.context.user;
+
+import com.hys.app.framework.security.model.Clerk;
+import com.hys.app.framework.security.model.Guide;
+
+/**
+ * 用户上下文
+ * Created by kingapex on 2018/3/12.
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.0.0
+ * 2018/3/12
+ */
+public class ChainUserContext {
+
+
+    /**
+     * 获取导购
+     */
+    public static Guide getGuide() {
+        return (Guide) UserContext.getSeller();
+    }
+
+    /**
+     * 获取收银员
+     */
+    public static Clerk getCashier() {
+        return (Clerk) UserContext.getSeller();
+    }
+
+
+}

+ 48 - 0
framework/src/main/java/com/hys/app/framework/context/user/DefaultUserHolder.java

@@ -0,0 +1,48 @@
+package com.hys.app.framework.context.user;
+
+import com.hys.app.framework.security.model.Buyer;
+import com.hys.app.framework.security.model.Seller;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+/**
+ * 默认用户holder
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-05-28
+ */
+@Service("userHolder")
+public class DefaultUserHolder implements  UserHolder{
+
+    @Override
+    public Seller getSeller() {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication == null) {
+            return null;
+        }
+        Object someOne = authentication.getDetails();
+        if (someOne != null && someOne instanceof Seller) {
+            return (Seller) someOne;
+        }
+        return null;
+    }
+
+    @Override
+    public Buyer getBuyer() {
+
+
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication == null) {
+            return null;
+        }
+        //如果含有买家权限则读取卖家信息并返回
+        Object someOne = authentication.getDetails();
+        if (someOne != null && someOne instanceof Buyer) {
+            return (Buyer) someOne;
+        }
+
+        return null;
+    }
+}

+ 42 - 0
framework/src/main/java/com/hys/app/framework/context/user/ParameterServletRequestWrapper.java

@@ -0,0 +1,42 @@
+package com.hys.app.framework.context.user;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ParameterServletRequestWrapper extends HttpServletRequestWrapper {
+    private final Map<String, String[]> parameterMap;
+
+    public ParameterServletRequestWrapper(HttpServletRequest request, Map<String, String[]> newParams) {
+        super(request);
+        parameterMap = new HashMap<>();
+        parameterMap.putAll(newParams);
+    }
+
+    @Override
+    public String getParameter(String name) {
+        String[] values = parameterMap.get(name);
+        if (values == null || values.length == 0) {
+            return null;
+        }
+        return values[0];
+    }
+
+    @Override
+    public Map<String, String[]> getParameterMap() {
+        return Collections.unmodifiableMap(parameterMap);
+    }
+
+    @Override
+    public Enumeration<String> getParameterNames() {
+        return Collections.enumeration(parameterMap.keySet());
+    }
+
+    @Override
+    public String[] getParameterValues(String name) {
+        return parameterMap.get(name);
+    }
+}

+ 57 - 0
framework/src/main/java/com/hys/app/framework/context/user/SnakeCaseInterceptor.java

@@ -0,0 +1,57 @@
+package com.hys.app.framework.context.user;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Enumeration;
+
+@Component
+public class SnakeCaseInterceptor implements HandlerInterceptor {
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        Map<String, String[]> updatedParams = new HashMap<>(request.getParameterMap());
+        Enumeration<String> paramNames = request.getParameterNames();
+
+        while (paramNames.hasMoreElements()) {
+            String paramName = paramNames.nextElement();
+            String snakeCaseName = toSnakeCase(paramName);
+            if (!paramName.equals(snakeCaseName)) {
+                updatedParams.put(snakeCaseName, updatedParams.remove(paramName));
+            }
+        }
+
+        // 替换HttpServletRequest中的参数为下划线命名
+        ParameterServletRequestWrapper wrappedRequest = new ParameterServletRequestWrapper(request, updatedParams);
+        request = wrappedRequest;
+
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+        // 可以在这里处理响应逻辑
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        // 请求完成后的清理工作
+    }
+
+    private String toSnakeCase(String s) {
+        StringBuilder builder = new StringBuilder();
+        for (char c : s.toCharArray()) {
+            if (Character.isUpperCase(c)) {
+                builder.append('_').append(Character.toLowerCase(c));
+            } else {
+                builder.append(c);
+            }
+        }
+        return builder.toString();
+    }
+}

+ 74 - 0
framework/src/main/java/com/hys/app/framework/context/user/UserContext.java

@@ -0,0 +1,74 @@
+package com.hys.app.framework.context.user;
+
+import com.hys.app.framework.context.app.AppTypeContext;
+import com.hys.app.framework.context.app.AppTypeEnum;
+import com.hys.app.framework.security.model.Buyer;
+import com.hys.app.framework.security.model.Seller;
+
+/**
+ * 用户上下文
+ * Created by kingapex on 2018/3/12.
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.0.0
+ * 2018/3/12
+ */
+public class UserContext {
+
+    private static UserHolder userHolder;
+
+
+    private static UserHolder chainUserHolder;
+
+    public static void setChainHolder(UserHolder user) {
+        chainUserHolder = user;
+    }
+
+    public static void setHolder(UserHolder user) {
+        userHolder = user;
+    }
+
+    /**
+     * 获取当前卖家
+     *
+     * @return
+     */
+    public static Seller getSeller() {
+        return userHolder.getSeller();
+    }
+
+    /**
+     * 为了方便在单元测试中测试已登录的情况,请使用此属性
+     * 如果此属性有值,买家上下文中将会直接返回此模拟对象
+     */
+    public static Buyer mockBuyer = null;
+
+    /**
+     * 获取当前买家
+     *
+     * @return
+     */
+    public static Buyer getBuyer() {
+
+        //如果有模拟对象,会直接返回此模拟对象
+        if (mockBuyer != null) {
+            return mockBuyer;
+        }
+
+        //**********************************************
+        //因为收银端登录后 需要通过会员对象获取结算信息
+        //普通会员端登录 则不需要获取结算信息
+        //所以这里将两个对象进行区分
+        AppTypeEnum appTypeEnum = AppTypeContext.get();
+
+        if (AppTypeEnum.Cashier.equals(appTypeEnum) || AppTypeEnum.Guide.equals(appTypeEnum)) {
+            return chainUserHolder.getBuyer();
+        } else {
+            return userHolder.getBuyer();
+        }
+
+    }
+
+
+}

+ 28 - 0
framework/src/main/java/com/hys/app/framework/context/user/UserHolder.java

@@ -0,0 +1,28 @@
+package com.hys.app.framework.context.user;
+
+import com.hys.app.framework.security.model.Buyer;
+import com.hys.app.framework.security.model.Seller;
+
+/**
+ * 用户信息hold接口
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-05-28
+ */
+public interface UserHolder {
+
+    /**
+     * 获取seller
+     * @return
+     */
+    Seller getSeller();
+
+    /**
+     * 获取buyer
+     * @return
+     */
+    Buyer getBuyer();
+
+
+}

+ 60 - 0
framework/src/main/java/com/hys/app/framework/database/BaseQueryParam.java

@@ -0,0 +1,60 @@
+package com.hys.app.framework.database;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 基础查询参数
+ *
+ * @author cs
+ * @since v7.3.0
+ * 2022/10/20
+ */
+public class BaseQueryParam {
+
+    /**
+     * 不分页
+     */
+    public static final long PAGE_NONE = -1L;
+
+    /**
+     * 页码
+     */
+    @ApiModelProperty(name = "page_no", value = "页码")
+    @NotNull(message = "页码不能为空")
+    private Long pageNo = 1L;
+
+    /**
+     * 页大小
+     */
+    @ApiModelProperty(name = "page_size", value = "页大小")
+    @NotNull(message = "页大小不能为空")
+    private Long pageSize = 10L;
+
+    public String getKeyWord() {
+        return keyWord;
+    }
+
+    public void setKeyWord(String keyWord) {
+        this.keyWord = keyWord;
+    }
+
+    private String keyWord;
+
+    public Long getPageNo() {
+        return pageNo;
+    }
+
+    public void setPageNo(Long pageNo) {
+        this.pageNo = pageNo;
+    }
+
+    public Long getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Long pageSize) {
+        this.pageSize = pageSize;
+    }
+}

+ 53 - 0
framework/src/main/java/com/hys/app/framework/database/ColumnMeta.java

@@ -0,0 +1,53 @@
+package com.hys.app.framework.database;
+
+/**
+ * 列数据
+ * @author Snow create in 2018/3/28
+ * @version v2.0
+ * @since v7.0.0
+ */
+public class ColumnMeta {
+
+    /**
+     * 主键名字
+     */
+    private String primaryKeyName;
+
+
+    /**
+     * 字段名
+     */
+    private Object[] names;
+
+
+    /**
+     * 字段值
+     */
+    private Object[] values;
+
+
+
+    public Object[] getNames() {
+        return names;
+    }
+
+    public void setNames(Object[] names) {
+        this.names = names;
+    }
+
+    public Object[] getValues() {
+        return values;
+    }
+
+    public void setValues(Object[] values) {
+        this.values = values;
+    }
+
+    public String getPrimaryKeyName() {
+        return primaryKeyName;
+    }
+
+    public void setPrimaryKeyName(String primaryKeyName) {
+        this.primaryKeyName = primaryKeyName;
+    }
+}

+ 37 - 0
framework/src/main/java/com/hys/app/framework/database/DBRuntimeException.java

@@ -0,0 +1,37 @@
+package com.hys.app.framework.database;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 数据库操作运行期异常
+ * @author kingapex
+ * 2010-1-6下午06:16:47
+ */
+public class DBRuntimeException extends RuntimeException {
+
+ 
+	private static final long serialVersionUID = -368646349014580765L;
+
+	private final Logger logger = LoggerFactory.getLogger(getClass());
+
+	
+	public DBRuntimeException(String message){
+		super(message);
+	}
+	public DBRuntimeException(Exception e,String sql) {
+		
+		super("数据库运行期异常");
+		e.printStackTrace();
+		logger.error("数据库运行期异常,相关sql语句为"+ sql);
+	}
+	
+	
+	public DBRuntimeException(String message,String sql) {
+		
+		super(message);
+		logger.error(message+",相关sql语句为"+ sql);
+	}
+	
+	
+}

+ 271 - 0
framework/src/main/java/com/hys/app/framework/database/DaoSupport.java

@@ -0,0 +1,271 @@
+package com.hys.app.framework.database;
+
+import org.springframework.jdbc.core.RowMapper;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 数据库操作支撑接口
+ * @author Snow create in 2018/3/21
+ * @version v2.0
+ * @since v7.0.0
+ */
+public interface DaoSupport {
+
+	/**
+	 * 执行sql语句
+	 * @param sql	sql语句
+	 * @param args	参数组
+	 */
+	int execute(String sql, Object... args) ;
+
+	/**
+	 * 查询单一结果集<br/>
+	 * 并将结果转为<code>int</code>型返回
+	 * @param sql 查询的sql语句,确定结果为一行一列,且为数字型
+	 * @param args 对应sql语句中的参数值
+	 * @return
+	 */
+	Integer queryForInt(String sql, Object... args);
+
+	/**
+	 * 查询单一结果集<br/>
+	 * 并将结果转为<code>long</code>型返回
+	 * @param sql 查询的sql语句,确保结果为一行一列,且为数字型
+	 * @param args 对应sql语句中的参数值
+	 * @return
+	 */
+	Long queryForLong(String sql, Object... args);
+
+
+	/**
+	 * 查询单一结果集<br/>
+	 * 并将结果转为<code>float</code>型返回
+	 * @param sql 查询的sql语句,确保结果为一行一列,且为数字型
+	 * @param args 对应sql语句中的参数值
+	 * @return
+	 */
+	Float queryForFloat(String sql, Object... args);
+
+
+	/**
+	 * 查询单一结果集<br/>
+	 * 并将结果转为<code>dobule</code>型返回
+	 * @param sql 查询的sql语句,确保结果为一行一列,且为数字型
+	 * @param args 对应sql语句中的参数值
+	 * @return
+	 */
+	Double queryForDouble(String sql, Object... args);
+
+
+
+
+	/**
+	 * 查询String 结果
+	 * @param sql 查询语句
+	 * @param args 参数
+	 * @return String型的结果
+	 */
+	String queryForString(String sql, Object... args);
+
+	/**
+	 * 查询单一结果集<br/>
+	 * 并将结果转为<code>Map</code>对象返回
+	 * @param sql 查询的sql语句
+	 * @param args 对应sql语句中的参数值
+	 * @return 以结果集中的列为key,值为value的<code>Map</code>
+	 */
+	@SuppressWarnings("unchecked")
+	Map queryForMap(String sql, Object... args) ;
+
+	/**
+	 * 查询多行结果集<br/>
+	 * 并将结果转为<code>List<Map></code>
+	 * @param sql 查询的sql语句
+	 * @param args 对应sql语句中的参数值
+	 * @return  列表中元素为<code>Map</code>的<code>List</code>,<br/>Map结构:以结果集中的列为key,值为value,
+	 */
+	@SuppressWarnings("unchecked")
+	List  queryForList(String sql, Object... args);
+
+
+
+	/**
+	 * 查询多行结果集<br/>
+	 * 并将结果转为<code>List<T></code>
+	 * @param sql 查询的sql语句
+	 * @param clazz <code><T></code>的Class对象
+	 * @param args 对应sql语句中的参数值
+	 * @return  列表中元素为<code>T</code>的<code>List</code>
+	 */
+	<T> List<T> queryForList(String sql, Class<T> clazz, Object... args);
+
+
+	/**
+	 * 使用rowmapper查询多行结果集<br/>
+	 * 并将结果转为<code>List<T></code>
+	 * @param sql 查询的sql语句
+	 * @param rowMapper mapper
+	 * @param args  查询参数
+	 * @param <T>   列表中元素类型
+	 * @return
+	 */
+	<T> List<T> queryForList(String sql, RowMapper<T> rowMapper, Object... args);
+
+
+	/**
+	 * 分页查询多行结果集<br/>
+	 * @param sql 查询的sql语句
+	 * @param pageNo 查询的起始页
+	 * @param pageSize  每页数量
+	 * @param args  对应sql语句中的参数值
+	 * @return 列表中元素为<code>Map</code>的<code>List</code>,<br/>Map结构:以结果集中的列为key,值为value,
+	 */
+	@SuppressWarnings("unchecked")
+	<T> List<Map> queryForListPage(String sql, long pageNo, long pageSize, Object... args);
+
+
+	/**
+	 * 分页查询
+	 * @param sql  查询的sql语句
+	 * @param pageNo 查询的起始页
+	 * @param pageSize  每页数量
+	 * @param args  对应sql语句中的参数值
+	 * @return 分页结果集对象
+	 */
+	WebPage queryForPage(String sql, long pageNo, long pageSize, Object... args) ;
+
+	/**
+	 * 分页查询
+	 * @param sql  查询的sql语句
+	 * @param countSql 用于查询总记录数的sql语句
+	 * @param pageNo 查询的起始页
+	 * @param pageSize  每页数量
+	 * @param args  对应sql语句中的参数值
+	 * @return 分页结果集对象
+	 */
+	WebPage queryForPage(String sql, String countSql, long pageNo, long pageSize, Object... args);
+
+
+	/**
+	 * 分页查询
+	 * @param sql 查询的sql语句
+	 * @param pageNo 查询的起始页
+	 * @param pageSize 每页数量
+	 * @param clazz  <code><T></code>的Class对象
+	 * @param args 对应sql语句中的参数值
+	 * @return
+	 */
+	<T> WebPage queryForPage(String sql, long pageNo, long pageSize, Class<T> clazz, Object... args);
+
+	/**
+	 * 更新数据
+	 * @param table 表名
+	 * @param fields 字段-值Map
+	 * @param where 更新条件(字段-值Map)
+	 */
+	@SuppressWarnings("unchecked")
+	int update(String table, Map fields, Map<String,?> where);
+
+	/**
+	 * 批量更新数据
+	 * @param sql 要执行的sql
+	 * @param batchArgs 相应的参数
+	 * @return
+	 */
+	int[] batchUpdate(String sql,List<Object[]> batchArgs);
+
+	/**
+	 * 批量更新数据
+	 * @param sql 要执行的sql数组
+	 * @return
+	 */
+	int[] batchUpdate(String... sql);
+
+
+	/**
+	 * 更新数据
+	 * @param table 表名
+	 * @param po 要更新的对象,保证对象的属性名和字段名对应
+	 * @param where 更新条件(字段-值Map)
+	 */
+	@SuppressWarnings("unchecked")
+	int update(String table, Object po, Map<String,?> where);
+
+
+
+	/**
+	 * 新增数据
+	 * @param table  表名
+	 * @param fields 字段-值Map
+	 */
+	@SuppressWarnings("unchecked")
+	void insert(String table, Map fields);
+
+	/**
+	 * 新增数据
+	 * @param table 表名
+	 * @param po 要新增的对象,保证对象的属性名和字段名对应
+	 */
+	void insert(String table, Object po);
+
+	/**
+	 * 读取最后插入的主键id
+	 * @param table	表名
+	 * @return
+	 */
+	long getLastId(String table);
+
+
+	/**
+	 * 执行带分页的sql语句
+	 * @param sql	sql语句
+	 * @param page	页数
+	 * @param pageSize	条数
+	 * @return
+	 */
+	String buildPageSql(String sql, long page, long pageSize);
+
+	/**
+	 * 新增数据
+	 * @param t  DO对象
+	 * @param <T>
+	 */
+	<T> void insert(T t);
+
+	/**
+	 * 修改数据
+	 * @param model	DO对象
+	 * @param id	主键ID
+	 * @param <T>
+	 */
+	<T> void update(T model,Long id);
+
+	/**
+	 * 删除数据
+     * @param <T>
+     * @param clazz    DO对象的class
+     * @param id    主键ID
+     */
+	<T> void delete(Class<T> clazz, Long id);
+
+	/**
+	 * 查询一行数据
+	 * @param <T>
+	 * @param clazz    DO对象的class
+	 * @param id    主键ID
+	 * @return
+	 */
+	<T> T queryForObject(Class<T> clazz, Long id);
+
+
+	/**
+	 * 查询一个对象
+	 * @param sql 查询 sql
+	 * @param args  sql中的查询 参数
+	 * @param clazz 对象的类型
+	 * @return 数据转后的对象
+	 */
+	<T> T queryForObject(String sql, Class<T> clazz, Object... args);
+}

+ 56 - 0
framework/src/main/java/com/hys/app/framework/database/DataMeta.java

@@ -0,0 +1,56 @@
+package com.hys.app.framework.database;
+
+import java.util.Arrays;
+
+/**
+ * 元数据
+ * @author Snow create in 2018/3/28
+ * @version v2.0
+ * @since v7.0.0
+ */
+public class DataMeta {
+
+    /**
+     * sql 语句
+     */
+    private String sql;
+
+    /**
+     * sql 语句中需要的参数
+     */
+    private Object[] paramter;
+
+    private Long primaryValue;
+
+    public String getSql() {
+        return sql;
+    }
+
+    public void setSql(String sql) {
+        this.sql = sql;
+    }
+
+    public Object[] getParamter() {
+        return paramter;
+    }
+
+    public void setParamter(Object[] paramter) {
+        this.paramter = paramter;
+    }
+
+    public Long getPrimaryValue() {
+        return primaryValue;
+    }
+
+    public void setPrimaryValue(Long primaryValue) {
+        this.primaryValue = primaryValue;
+    }
+
+    @Override
+    public String toString() {
+        return "DataMeta{" +
+                "sql='" + sql + '\'' +
+                ", paramter=" + Arrays.toString(paramter) +
+                '}';
+    }
+}

+ 200 - 0
framework/src/main/java/com/hys/app/framework/database/DataSourceConfig.java

@@ -0,0 +1,200 @@
+package com.hys.app.framework.database;
+
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import com.hys.app.framework.database.impl.DaoSupportImpl;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+
+/**
+ * Created by kingapex on 2018/3/6.
+ * 数据源配置
+ * @author kingapex
+ * @version 1.0
+ * @since 7.0.0
+ * 2018/3/6
+ */
+@Configuration
+@EnableTransactionManagement
+public class DataSourceConfig {
+
+    /*----------------------------------------------------------------------------*/
+    /*                           DaoSupport配置                                    */
+    /*----------------------------------------------------------------------------*/
+
+    /**
+     * 商品daoSupport
+     * @param jdbcTemplate 商品jdbcTemplate
+     * @return
+     */
+    @Bean(name = "goodsDaoSupport")
+    @Primary
+    public DaoSupport goodsDaoSupport(JdbcTemplate jdbcTemplate) {
+        DaoSupport daosupport = new DaoSupportImpl(jdbcTemplate);
+        return daosupport;
+    }
+
+    /**
+     * 交易daoSupport
+     * @param jdbcTemplate 交易jdbcTemplate
+     * @return
+     */
+    @Bean(name = "tradeDaoSupport")
+    public DaoSupport tradeDaoSupport( JdbcTemplate jdbcTemplate) {
+        DaoSupport daosupport = new DaoSupportImpl(jdbcTemplate);
+        return daosupport;
+    }
+
+
+
+    /**
+     * 会员daoSupport
+     * @param jdbcTemplate 会员jdbcTemplate
+     * @return
+     */
+    @Bean(name = "memberDaoSupport")
+    public DaoSupport memberDaoSupport(JdbcTemplate jdbcTemplate) {
+        DaoSupport daosupport = new DaoSupportImpl(jdbcTemplate);
+        return daosupport;
+    }
+
+    /**
+     * 系统daoSupport
+     * @param jdbcTemplate 系统 jdbcTemplate
+     * @return
+     */
+    @Bean(name = "systemDaoSupport")
+    public DaoSupport systemDaoSupport(JdbcTemplate jdbcTemplate) {
+        DaoSupport daosupport = new DaoSupportImpl(jdbcTemplate);
+        return daosupport;
+    }
+
+
+    /**
+     * 统计 daoSupport
+     * @param jdbcTemplate 统计jdbcTemplate
+     * @return
+     */
+    @Bean(name = "sssDaoSupport")
+    public DaoSupport sssDaoSupport(JdbcTemplate jdbcTemplate) {
+        DaoSupport daosupport = new DaoSupportImpl(jdbcTemplate);
+        return daosupport;
+    }
+
+
+    /**
+     * 分销 daoSupport
+     * @param jdbcTemplate 分销jdbcTemplate
+     * @return
+     */
+    @Bean(name = "distributionDaoSupport")
+    public DaoSupport distributionDaoSupport( JdbcTemplate jdbcTemplate) {
+        DaoSupport daosupport = new DaoSupportImpl(jdbcTemplate);
+        return daosupport;
+    }
+
+    @Bean
+    public JdbcTemplate jdbcTemplate(final DataSource dataSource) {
+        return new JdbcTemplate(dataSource);
+    }
+
+
+    /*----------------------------------------------------------------------------*/
+    /*                           事务配置                                         */
+    /*----------------------------------------------------------------------------*/
+
+    @Bean
+    public PlatformTransactionManager transactionManager(DataSource dataSource) {
+        return new DataSourceTransactionManager(dataSource);
+    }
+
+    /**
+     * 会员事务
+     * @return
+     */
+    @Bean
+    public PlatformTransactionManager memberTransactionManager(PlatformTransactionManager transactionManager) {
+        return transactionManager;
+    }
+
+    /**
+     * 商品事务
+     * @return
+     */
+    @Bean
+    @Primary
+    public PlatformTransactionManager goodsTransactionManager(PlatformTransactionManager transactionManager) {
+        return transactionManager;
+    }
+
+    /**
+     * 交易事务
+     * @return
+     */
+    @Bean
+    public PlatformTransactionManager tradeTransactionManager(PlatformTransactionManager transactionManager) {
+        return transactionManager;
+    }
+
+    /**
+     * 系统事务
+     * @return
+     */
+    @Bean
+    public PlatformTransactionManager systemTransactionManager(PlatformTransactionManager transactionManager) {
+        return transactionManager;
+    }
+
+    /**
+     * 统计事务
+     * @return
+     */
+    @Bean
+    public PlatformTransactionManager sssTransactionManager(PlatformTransactionManager transactionManager) {
+        return transactionManager;
+    }
+
+
+    /**
+     * 分销事务
+     * @return
+     */
+    @Bean
+    public PlatformTransactionManager distributionTransactionManager(PlatformTransactionManager transactionManager) {
+        return transactionManager;
+    }
+
+    /**
+     * mybatis plus 分页设置
+     * @return
+     */
+//    @Bean
+//    public PaginationInnerInterceptor paginationInterceptor() {
+//        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
+//        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
+//        // paginationInterceptor.setOverflow(false);
+//        // 设置最大单页限制数量,默认 500 条,-1 不受限制
+//        // paginationInterceptor.setLimit(500);
+//        // 开启 count 的 join 优化,只针对部分 left join
+//        return paginationInterceptor;
+//    }
+
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
+        // 分页插件
+        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
+        //乐观锁插件
+        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+        return mybatisPlusInterceptor;
+    }
+
+}

+ 24 - 0
framework/src/main/java/com/hys/app/framework/database/DoubleMapper.java

@@ -0,0 +1,24 @@
+package com.hys.app.framework.database;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.springframework.jdbc.core.RowMapper;
+
+/**
+ * 浮点型mapper
+ * @author kingapex
+ * @version v1.0
+ * @since v7.0.0
+ * 2018年3月23日 上午10:26:41
+ */
+public class DoubleMapper implements RowMapper<Double> {
+
+	
+	@Override
+    public Double mapRow(ResultSet rs, int rowNum) throws SQLException {
+		Double dobule = new Double(rs.getDouble(1));
+		return dobule;
+	}
+
+}

+ 29 - 0
framework/src/main/java/com/hys/app/framework/database/DynamicField.java

@@ -0,0 +1,29 @@
+package com.hys.app.framework.database;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.hys.app.framework.database.annotation.NotDbField;
+
+
+/**
+ * 动态字段
+ * @author kingapex
+ *2012-5-5下午12:46:37
+ */
+public class DynamicField {
+	
+	private Map<String,Object> fields;
+	public DynamicField(){
+		fields = new HashMap<String, Object>();
+	}
+	
+	public void addField(String name,Object value){
+		fields.put(name, value);
+	}
+	
+	@NotDbField
+	public Map<String,Object> getFields(){
+		return fields;
+	}
+}

+ 23 - 0
framework/src/main/java/com/hys/app/framework/database/LongMapper.java

@@ -0,0 +1,23 @@
+package com.hys.app.framework.database;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.springframework.jdbc.core.RowMapper;
+
+/**
+ * Long mapper
+ * @author kingapex
+ * @version v1.0
+ * @since v7.0.0
+ * 2018年3月23日 上午10:26:41
+ */
+public class LongMapper implements RowMapper<Long> {
+
+	@Override
+    public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
+		Long  v = rs.getLong(1);
+		return v;
+	}
+
+}

+ 25 - 0
framework/src/main/java/com/hys/app/framework/database/ObjectNotFoundException.java

@@ -0,0 +1,25 @@
+package com.hys.app.framework.database;
+
+/**
+ * 对像未找到异常<br>
+ * 多用于根据某id查询一条记录,但此记录不存在
+ * @author kingapex
+ *2010-4-9上午09:09:20
+ */
+public class ObjectNotFoundException extends DBRuntimeException {
+
+	public ObjectNotFoundException(String message) {
+		super(message);
+	}
+		
+	public ObjectNotFoundException(Exception e, String sql) {
+		super(e, sql);
+	 
+	}
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3403302876974180460L;
+
+}

+ 42 - 0
framework/src/main/java/com/hys/app/framework/database/SqlMetaBuilder.java

@@ -0,0 +1,42 @@
+package com.hys.app.framework.database;
+
+/**
+ * 元数据构建器
+ * 构建模型构建执行sql的构建器
+ * @author Snow create in 2018/3/21
+ * @version v2.0
+ * @since v7.0.0
+ */
+public interface SqlMetaBuilder {
+
+    /**
+     * 通过一个模型,获取新增数据的元数据信息
+     * @param <T> model的类型
+     * @param model 模型
+     * @return insert的元数据
+     */
+    <T> DataMeta insert(T model);
+
+    /**
+     * 通过一个模型,获取修改数据的元数据信息
+     * @param <T>  model的类型
+     * @param model 模型
+     * @param id 主键值
+     * @return update的元数据
+     */
+    <T> DataMeta update(T model, Long id);
+
+    /**
+     * 通过Class,获取查询一行数据的sql
+     * @param clazz 模型的类型
+     * @return 查询一个模型的sql,其中已经拼接好根据主键查询的的where条件
+     */
+    String queryForModel(Class clazz);
+
+    /**
+     * 通过Class,获取删除一行数据的sql
+     * @param clazz 模型的类型
+     * @return 删除的sql语句,其中已经拼接好根据主键删除的where条件
+     */
+    String delete(Class clazz);
+}

+ 24 - 0
framework/src/main/java/com/hys/app/framework/database/StringMapper.java

@@ -0,0 +1,24 @@
+package com.hys.app.framework.database;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.springframework.jdbc.core.RowMapper;
+
+/**
+ * 字符串型mapper
+ * @author kingapex
+ * @version v1.0
+ * @since v7.0.0
+ * 2018年3月23日 上午10:26:41
+ */
+public class StringMapper implements  RowMapper<String> {
+
+	
+	@Override
+    public String mapRow(ResultSet rs, int rowNum) throws SQLException {
+		String str = rs.getString(1);
+		return str;
+	}
+
+}

+ 148 - 0
framework/src/main/java/com/hys/app/framework/database/WebPage.java

@@ -0,0 +1,148 @@
+package com.hys.app.framework.database;
+
+import com.hys.app.framework.util.group.BaseGroup;
+import com.fasterxml.jackson.annotation.JsonView;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 分页对象. 包含当前页数据及分页信息如总记录数.
+ *
+ * @param <T> 数据类型
+ * @author kingapex
+ * @version v1.0
+ * @since v7.0.0
+ * 2017年8月15日 上午10:55:08
+ */
+@ApiModel(value = "数据分页对象")
+@JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class)
+public class WebPage<T> implements Serializable {
+
+	/**
+	 * 数据列表
+	 */
+	@ApiModelProperty(value = "列表数据")
+	@JsonView(BaseGroup.class)
+	private List<T> data;
+
+	/**
+	 * 当前页码
+	 */
+	@ApiModelProperty(value = "当前页码")
+	@JsonView(BaseGroup.class)
+	private Long pageNo;
+
+
+	/**
+	 * 分页大小
+	 */
+	@ApiModelProperty(value = "分页大小")
+	@JsonView(BaseGroup.class)
+	private Long pageSize;
+
+	/**
+	 * 总计录数
+	 */
+	@ApiModelProperty(value = "总计录数")
+	@JsonView(BaseGroup.class)
+	private Long dataTotal;
+
+
+	/**
+	 * 构造方法
+	 *
+	 * @param data      数据列表
+	 * @param pageNo    当前页码
+	 * @param pageSize  页大小
+	 * @param dataTotal 总计录数
+	 */
+	public WebPage(Long pageNo, Long dataTotal, Long pageSize, List<T> data) {
+		this.data = data;
+		this.pageNo = pageNo;
+		this.pageSize = pageSize;
+		this.dataTotal = dataTotal;
+	}
+
+
+	public WebPage() {
+	}
+
+	public List<T> getData() {
+		return data;
+	}
+
+	public void setData(List<T> data) {
+		this.data = data;
+	}
+
+	public Long getPageNo() {
+		return pageNo;
+	}
+
+	public void setPageNo(Long pageNo) {
+		this.pageNo = pageNo;
+	}
+
+	public Long getPageSize() {
+		return pageSize;
+	}
+
+	public void setPageSize(Long pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	public Long getDataTotal() {
+		return dataTotal;
+	}
+
+	public void setDataTotal(Long dataTotal) {
+		this.dataTotal = dataTotal;
+	}
+
+	@Override
+	public String toString() {
+		return "WebPage{" +
+				"data=" + data +
+				", pageNo=" + pageNo +
+				", pageSize=" + pageSize +
+				", dataTotal=" + dataTotal +
+				'}';
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (this == o) {
+			return true;
+		}
+		if (o == null || getClass() != o.getClass()) {
+			return false;
+		}
+
+		WebPage<?> page = (WebPage<?>) o;
+
+		if (data != null ? !data.equals(page.data) : page.data != null) {
+			return false;
+		}
+		if (pageNo != null ? !pageNo.equals(page.pageNo) : page.pageNo != null) {
+			return false;
+		}
+		if (pageSize != null ? !pageSize.equals(page.pageSize) : page.pageSize != null) {
+			return false;
+		}
+		return dataTotal != null ? dataTotal.equals(page.dataTotal) : page.dataTotal == null;
+	}
+
+	@Override
+	public int hashCode() {
+		int result = data != null ? data.hashCode() : 0;
+		result = 31 * result + (pageNo != null ? pageNo.hashCode() : 0);
+		result = 31 * result + (pageSize != null ? pageSize.hashCode() : 0);
+		result = 31 * result + (dataTotal != null ? dataTotal.hashCode() : 0);
+		return result;
+	}
+}

+ 21 - 0
framework/src/main/java/com/hys/app/framework/database/annotation/Column.java

@@ -0,0 +1,21 @@
+package com.hys.app.framework.database.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标识为数据库的字段
+ * @author Snow
+ */
+@Target(ElementType.FIELD) 
+@Retention(RetentionPolicy.RUNTIME) 
+public @interface Column {
+
+	String name() default "";
+
+	boolean allowNullUpdate() default  false;
+
+
+}

+ 17 - 0
framework/src/main/java/com/hys/app/framework/database/annotation/Id.java

@@ -0,0 +1,17 @@
+package com.hys.app.framework.database.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标识为此表的主键ID
+ * @author Snow
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Id {
+
+    String name() default "";
+}

+ 17 - 0
framework/src/main/java/com/hys/app/framework/database/annotation/NotDbField.java

@@ -0,0 +1,17 @@
+package com.hys.app.framework.database.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标识不是数据库读写的字段
+ * @author kingapex
+ * 2010-1-22下午04:08:58
+ */
+@Retention(RetentionPolicy.RUNTIME) 
+@Target(ElementType.METHOD) 
+public @interface NotDbField {
+
+}

+ 17 - 0
framework/src/main/java/com/hys/app/framework/database/annotation/PrimaryKeyField.java

@@ -0,0 +1,17 @@
+package com.hys.app.framework.database.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标识不是数据库读写的字段
+ * @author kingapex
+ * 2010-1-22下午04:08:58
+ */
+@Retention(RetentionPolicy.RUNTIME) 
+@Target(ElementType.METHOD) 
+public @interface PrimaryKeyField {
+
+}

+ 21 - 0
framework/src/main/java/com/hys/app/framework/database/annotation/Table.java

@@ -0,0 +1,21 @@
+package com.hys.app.framework.database.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标识为数据库的表名
+ * @author Snow
+ */
+@Target(ElementType.TYPE) 
+@Retention(RetentionPolicy.RUNTIME) 
+public @interface Table {
+
+	/**
+	 * 数据库的表名
+	 * @return
+	 */
+	String name();
+}

+ 559 - 0
framework/src/main/java/com/hys/app/framework/database/impl/DaoSupportImpl.java

@@ -0,0 +1,559 @@
+package com.hys.app.framework.database.impl;
+
+import com.hys.app.framework.database.*;
+import com.hys.app.framework.sncreator.SnCreator;
+import com.hys.app.framework.util.ReflectionUtil;
+import com.hys.app.framework.util.StringUtil;
+import org.apache.commons.collections.map.HashedMap;
+import org.apache.commons.lang.ArrayUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.util.Assert;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * jdbc数据库操作支撑实现类
+ * @author Snow create in 2018/3/21
+ * @version v2.0
+ * @since v7.0.0
+ */
+public class DaoSupportImpl implements DaoSupport {
+
+
+    private JdbcTemplate jdbcTemplate;
+
+	@Autowired
+	private SqlMetaBuilder sqlMetaBuilder;
+
+	private ThreadLocal<Long> lastIdThreadLocal = new ThreadLocal<>();
+
+    /**
+     * 日志记录
+     */
+	private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * order by 语句正则
+     */
+    private  static final Pattern ORDER_PATTERN = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
+
+    /**
+     * 删除select正则
+     */
+    private  static final Pattern REMOVE_SELECT_PATTERN  = Pattern.compile("\\(.*\\)", Pattern.CASE_INSENSITIVE);
+
+	public DaoSupportImpl(){
+	}
+
+    /**
+     * 实例化jdbcTemplate
+     */
+	public DaoSupportImpl(JdbcTemplate jdbcTemplate){
+		this.jdbcTemplate= jdbcTemplate;
+	}
+
+
+	@Override
+	public int execute(String sql, Object... args) {
+		try {
+			int rowNum = this.jdbcTemplate.update(sql, args);
+
+			return rowNum;
+		} catch (Exception e) {
+			throw new DBRuntimeException(e, sql);
+		}
+	}
+
+
+	@Override
+	public long getLastId(String table) {
+		return lastIdThreadLocal.get();
+	}
+
+	@Override
+	public void insert(String table, Map fields) {
+		String sql = "";
+
+		try {
+
+			Assert.hasText(table, "表名不能为空");
+			Assert.notEmpty(fields, "字段不能为空");
+			table = quoteCol(table);
+
+			Object[] cols = fields.keySet().toArray();
+			Object[] values = new Object[cols.length];
+
+			for (int i = 0; i < cols.length; i++) {
+				if (fields.get(cols[i]) == null) {
+					values[i] = null;
+				} else {
+					values[i] = fields.get(cols[i]);
+				}
+				cols[i] = quoteCol(cols[i].toString());
+			}
+
+			sql = "INSERT INTO " + table + " (" + StringUtil.implode(", ", cols);
+
+			sql = sql + ") VALUES (" + StringUtil.implodeValue(", ", values);
+
+			sql = sql + ")";
+			jdbcTemplate.update(sql, values);
+		} catch (Exception e) {
+			this.logger.error(e.getMessage(), e);
+			throw new DBRuntimeException(e, sql);
+		}
+	}
+
+	@Autowired
+	SnCreator snCreator;
+
+	int getSubCode(String table) {
+
+		return 1;
+	}
+	@Override
+	public void insert(String table, Object po) {
+		Long id = snCreator.create(getSubCode(table));
+
+		//设置刚刚主键值到 thread local
+		lastIdThreadLocal.set(id);
+		Map poMap = new HashedMap();
+		ColumnMeta columnMeta = ReflectionUtil.getColumnMeta(po);
+
+		poMap.put(columnMeta.getPrimaryKeyName(), id);
+		Object[] columnName = columnMeta.getNames();
+		Object[] columnValue = columnMeta.getValues();
+
+		for (int i = 0; i < columnValue.length; i++) {
+			poMap.put(columnName[i],columnValue[i]);
+		}
+
+		insert(table, poMap);
+	}
+
+	@Override
+	public Integer queryForInt(String sql, Object... args) {
+		try {
+			Integer value = jdbcTemplate.queryForObject(sql, Integer.class, args);
+			return  value==null?0:value;
+		}catch(EmptyResultDataAccessException e){
+			return 0;
+		} catch (RuntimeException e) {
+			this.logger.error(e.getMessage(), e);
+			throw e;
+		}
+	}
+
+	@Override
+	public Float queryForFloat(String sql, Object... args) {
+		try {
+
+			Float value = jdbcTemplate.queryForObject(sql, Float.class, args);
+			return  value==null?0F:value;
+
+		} catch(EmptyResultDataAccessException e){
+			return 0F;
+		} catch (RuntimeException e) {
+			this.logger.error(e.getMessage(), e);
+			throw e;
+		}
+	}
+
+	@Override
+	public Long queryForLong(String sql, Object... args) {
+		try {
+			Long value = jdbcTemplate.queryForObject(sql, Long.class, args);
+			return  value==null?0L:value;
+		} catch(EmptyResultDataAccessException e){
+			return 0L;
+		} catch (RuntimeException e) {
+			this.logger.error(e.getMessage(), e);
+			throw e;
+		}
+
+	}
+
+	@Override
+	public Double queryForDouble(String sql, Object... args) {
+		try {
+
+			Double value = jdbcTemplate.queryForObject(sql, Double.class, args);
+			return  value==null?0D:value;
+
+		} catch(EmptyResultDataAccessException e){
+			return 0D;
+		} catch (RuntimeException e) {
+			this.logger.error(e.getMessage(), e);
+			throw e;
+		}
+	}
+
+	@Override
+	public String queryForString(String sql, Object... args) {
+
+		String s = "";
+		try {
+			s = this.jdbcTemplate.queryForObject(sql, String.class,args);
+		}catch (EmptyResultDataAccessException e){
+			return "";
+		}catch (RuntimeException e) {
+			logger.debug("查询sql:["+sql+"]出错",e);
+
+		}
+		return s;
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public List queryForList(String sql, Object... args) {
+		return this.jdbcTemplate.queryForList(sql, args);
+	}
+
+
+	@Override
+	public <T> List<T> queryForList(String sql, Class<T> clazz, Object... args) {
+
+		return this.jdbcTemplate.query(sql, new BeanPropertyRowMapper<T>(clazz), args);
+
+	}
+
+	@Override
+	public <T> List<T> queryForList(String sql, RowMapper<T> rowMapper, Object... args) {
+		return jdbcTemplate.query(sql,rowMapper,args);
+	}
+
+	@Override
+	public List queryForListPage(String sql, long pageNo, long pageSize, Object... args) {
+
+		try {
+			Assert.hasText(sql, "SQL语句不能为空");
+			Assert.isTrue(pageNo >= 1, "pageNo 必须大于等于1");
+			String listSql = this.buildPageSql(sql, pageNo, pageSize);
+			return queryForList(listSql, args);
+		} catch (Exception ex) {
+			throw new DBRuntimeException(ex, sql);
+		}
+
+	}
+
+	@Override
+	public Map queryForMap(String sql, Object... args) {
+		try {
+			Map map = this.jdbcTemplate.queryForMap(sql, args);
+
+				return map;
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			throw new ObjectNotFoundException(ex, sql);
+		}
+	}
+
+	@Override
+	public WebPage queryForPage(String sql, long pageNo, long pageSize, Object... args) {
+		String countSql = "SELECT COUNT(*) " + removeSelect(removeOrders(sql));
+		return this.queryForPage(sql, countSql, pageNo, pageSize, args);
+	}
+
+	@Override
+	public WebPage queryForPage(String sql, String countSql, long pageNo, long pageSize, Object... args) {
+		Assert.hasText(sql, "SQL语句不能为空");
+		Assert.isTrue(pageNo >= 1, "pageNo 必须大于等于1");
+		String listSql = buildPageSql(sql, pageNo, pageSize);
+
+		List list = queryForList(listSql, args);
+		Long totalCount = queryForLong(countSql, args);
+		return new WebPage(pageNo, totalCount, pageSize, list);
+
+	}
+	@Override
+	public <T> WebPage queryForPage(String sql, long pageNo, long pageSize, Class<T> clazz, Object... args) {
+
+		Assert.hasText(sql, "SQL语句不能为空");
+		Assert.isTrue(pageNo >= 1, "pageNo 必须大于等于1");
+		String listSql = buildPageSql(sql, pageNo, pageSize);
+		String countSql = "SELECT COUNT(*) " + removeSelect(removeOrders(sql));
+		List<T> list = this.queryForList(listSql, clazz, args);
+		Long totalCount = queryForLong(countSql, args);
+		return new WebPage(pageNo, totalCount, pageSize, list);
+
+	}
+
+	@Override
+	public int update(String table, Map fields, Map<String,?> where) {
+
+		Assert.hasText(table, "表名不能为空");
+		Assert.notEmpty(fields, "字段不能为空");
+		Assert.notEmpty(where, "where条件不能为空");
+
+		String whereSql = this.createWhereSql(where);
+
+		// 字段名
+		Object[] cols = fields.keySet().toArray();
+		String fieldSql = "";
+
+		for(int i=0;i<cols.length;i++){
+
+			fieldSql+= cols[i]+"=?";
+			 if(i!=cols.length-1){
+				 fieldSql+=",";
+			 }
+		}
+
+		// 字段值
+		Object[] values  = ArrayUtils.addAll(fields.values().toArray(),where.values().toArray());
+
+		String sql = "UPDATE " + table + " SET " +fieldSql  + " WHERE " + whereSql;
+
+		return this.jdbcTemplate.update(sql, values);
+
+	}
+
+	@Override
+	public int[] batchUpdate(String sql, List<Object[]> batchArgs) {
+		return jdbcTemplate.batchUpdate(sql, batchArgs);
+	}
+
+	@Override
+	public int[] batchUpdate(String... sql) {
+		return jdbcTemplate.batchUpdate(sql);
+	}
+
+	@Override
+	public int update(String table, Object po, Map<String,?> where) {
+
+		return update(table, ReflectionUtil.po2Map(po), where);
+	}
+
+
+	@Override
+	public String buildPageSql(String sql, long page, long pageSize) {
+
+		String sqlStr = null;
+
+		String dbType =  "mysql";
+
+		//防止魔法值
+		String mysqlStr = "mysql";
+		String sqlserverStr = "sqlserver";
+        String oracleStr = "oracle";
+
+		if (mysqlStr.equals(dbType)) {
+			sqlStr = sql + " LIMIT " + (page - 1) * pageSize + "," + pageSize;
+		} else if (oracleStr.equals(dbType)) {
+			StringBuffer localSql = new StringBuffer("SELECT * FROM (SELECT t1.*,rownum sn1 FROM (");
+			localSql.append(sql);
+			localSql.append(") t1) t2 WHERE t2.sn1 BETWEEN ");
+			localSql.append((page - 1) * pageSize + 1);
+			localSql.append(" AND ");
+			localSql.append(page * pageSize);
+			sqlStr = localSql.toString();
+		} else if (sqlserverStr.equals(dbType)) {
+			StringBuffer localSql = new StringBuffer();
+			// 找到order by 子句
+			String order = SqlPaser.findOrderStr(sql);
+
+			// 剔除order by 子句
+			if (order != null) {
+				sql = removeOrders(sql);
+			}
+			else {
+				// SQLServer分页必需有order by
+				// 子句,如果默认语句不包含order by,
+				// 自动以id降序,如果没有id字段会报错
+				order = "order by id desc";
+
+			}
+
+			// 拼装分页sql
+			localSql.append("select * from (");
+			localSql.append(SqlPaser.insertSelectField("ROW_NUMBER() Over(" + order + ") as rowNum", sql));
+			localSql.append(") tb where rowNum between ");
+			localSql.append((page - 1) * pageSize + 1);
+			localSql.append(" AND ");
+			localSql.append(page * pageSize);
+
+			return localSql.toString();
+
+		}
+
+		return sqlStr.toString();
+
+	}
+
+	/**
+	 * 格式化列名 只适用于Mysql
+	 *
+	 * @param col
+	 * @return
+	 */
+	private String quoteCol(String col) {
+		if (col == null || "".equals(col)) {
+			return "";
+		} else {
+			// if("2".equals(EopSetting.DBTYPE))//Oracle
+			// return "\"" + col + "\"";
+			// else if("1".equals(EopSetting.DBTYPE))//mysql
+			// return "`" + col + "`";
+			// else //mssql
+			// return "[" + col + "]";
+			return col;
+		}
+	}
+
+	/**
+	 * 格式化值 只适用于Mysql
+	 *
+	 * @param value
+	 * @return
+	 */
+	private String quoteValue(String value) {
+		if (value == null || "".equals(value)) {
+			return "''";
+		} else {
+			return "'" + value.replaceAll("'", "''") + "'";
+		}
+	}
+
+	private String getStr(int num, String str) {
+		StringBuffer sb = new StringBuffer("");
+		for (int i = 0; i < num; i++) {
+			sb.append(str);
+		}
+		return sb.toString();
+	}
+
+
+
+
+
+    @Override
+	public <T> void insert(T t){
+		DataMeta dataMeta  = this.sqlMetaBuilder.insert(t);
+		lastIdThreadLocal.set(dataMeta.getPrimaryValue());
+
+		String sql = dataMeta.getSql();
+		Object[] param = dataMeta.getParamter();
+		this.jdbcTemplate.update(sql,param);
+
+	}
+
+
+	@Override
+	public <T> void update(T model,Long id) {
+		DataMeta dataMeta  = this.sqlMetaBuilder.update(model,id);
+		String sql = dataMeta.getSql();
+		Object[] param = dataMeta.getParamter();
+		this.jdbcTemplate.update(sql,param);
+	}
+
+
+	@Override
+	public <T> void delete(Class<T> clazz, Long id) {
+		String sql = this.sqlMetaBuilder.delete(clazz);
+		Long[] ids = {id};
+		this.jdbcTemplate.update(sql,ids);
+	}
+
+
+	@Override
+	public <T> T  queryForObject(Class<T> clazz, Long id) {
+
+		String sql = this.sqlMetaBuilder.queryForModel(clazz);
+		Long[] ids = {id};
+		List<T> objList = this.queryForList(sql, clazz, ids);
+		if (objList.isEmpty()) {
+			return null;
+		}
+		return objList.get(0);
+	}
+
+
+	@Override
+	public <T> T queryForObject(String sql, Class<T> clazz, Object... args) {
+		List<T> objList = this.queryForList(sql, clazz, args);
+		if (objList.isEmpty()) {
+			return null;
+		}
+		return objList.get(0);
+	}
+
+
+    /**
+     * 去除hql的order by 子句,用于pagedQuery.
+     *
+     */
+    private String removeOrders(String hql) {
+        Assert.hasText(hql,"hql must hast text");
+
+        Matcher m = ORDER_PATTERN.matcher(hql);
+        StringBuffer sb = new StringBuffer();
+        while (m.find()) {
+            m.appendReplacement(sb, "");
+        }
+        m.appendTail(sb);
+        return sb.toString();
+    }
+
+    /**
+     * 去除sql的select 子句,未考虑union的情况,用于pagedQuery.
+     */
+    private String removeSelect(String sql) {
+
+        String groupBySql="group by";
+        sql = sql.toLowerCase();
+        if (sql.indexOf(groupBySql) != -1) {
+            return " from (" + sql + ") temp_table";
+        }
+
+        // FIXME 当查询中含有函数,比如SUM(),替换SQL出错
+        Matcher m = REMOVE_SELECT_PATTERN.matcher(sql);
+        StringBuffer sb = new StringBuffer();
+        while (m.find()) {
+            int c = m.end() - m.start();
+            m.appendReplacement(sb, getStr(c, "~"));
+        }
+        m.appendTail(sb);
+
+        String replacedSql = sb.toString();
+
+        int index = replacedSql.indexOf("from");
+
+        // 如果不存在
+        if (index == -1) {
+            index = replacedSql.indexOf("FROM");
+        }
+        return sql.substring(index);
+    }
+
+    /**
+     * 根据一个Map的条件,生成where 语句
+     * @param where  key为条件,value为条件值
+     * @return where 语句
+     */
+    private String createWhereSql(Map<String,?> where){
+
+        String whereSql = "";
+        if (where != null) {
+            Object[] whereCols = where.keySet().toArray();
+            for (int i = 0; i < whereCols.length; i++) {
+                StringBuffer str = new StringBuffer();
+                str.append(whereCols[i].toString());
+                str.append("=?");
+                whereCols[i] = str.toString();
+            }
+            whereSql += StringUtil.implode(" AND ", whereCols);
+        }
+
+        return whereSql;
+    }
+}

+ 39 - 0
framework/src/main/java/com/hys/app/framework/database/impl/FilterColumnMapRowMapper.java

@@ -0,0 +1,39 @@
+package com.hys.app.framework.database.impl;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.springframework.jdbc.core.ColumnMapRowMapper;
+import org.springframework.jdbc.support.JdbcUtils;
+
+/**
+ * map mapper
+ * @author kingapex
+ * @version v1.0
+ * @since v7.0.0
+ * 2018年3月23日 上午10:26:41
+ */
+public class FilterColumnMapRowMapper extends ColumnMapRowMapper {
+	private IRowMapperColumnFilter filter;
+	public FilterColumnMapRowMapper(IRowMapperColumnFilter filter){
+		this.filter = filter;
+	}
+	
+	@Override
+	public  Map<String, Object>  mapRow(ResultSet rs, int rowNum) throws SQLException {
+		ResultSetMetaData rsmd = rs.getMetaData();
+		int columnCount = rsmd.getColumnCount();
+		Map mapOfColValues = createColumnMap(columnCount);
+		for (int i = 1; i <= columnCount; i++) {
+			String key = getColumnKey(JdbcUtils.lookupColumnName(rsmd, i));
+			key  = key.toLowerCase();
+			Object obj = getColumnValue(rs, i);
+			mapOfColValues.put(key, obj);
+			//对此行结果集进行过滤
+			this.filter.filter(mapOfColValues, rs);
+		}
+		return mapOfColValues;
+	}
+}

+ 25 - 0
framework/src/main/java/com/hys/app/framework/database/impl/IRowMapperColumnFilter.java

@@ -0,0 +1,25 @@
+package com.hys.app.framework.database.impl;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+/**
+ * 数据库结果集过滤器
+ * 可对RowMapper的结果某或多列进行过滤 
+ * @author kingapex
+ *
+ */
+public interface IRowMapperColumnFilter {
+	
+	
+	/**
+	 * 对结果集的行进行过滤
+	 * @param colValues 结果集一行的map
+	 * @param rs 结果集
+	 * @throws  SQLException 可能的sql异常
+	 */
+    void filter(Map colValues, ResultSet rs) throws SQLException;
+	
+	
+}

+ 21 - 0
framework/src/main/java/com/hys/app/framework/database/impl/LowerCaseJdbcTemplate.java

@@ -0,0 +1,21 @@
+package com.hys.app.framework.database.impl;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+
+
+
+/**
+ * 覆写jdbctemlate ,使用LowerCaseColumnMapRowMapper
+ * @author kingapex
+ * 2010-6-13上午11:05:32
+ */
+public class LowerCaseJdbcTemplate extends JdbcTemplate {
+	@Override
+	protected RowMapper getColumnMapRowMapper() {
+
+		return new MySqlColumnMapRowMapper();
+
+	}
+
+}

+ 39 - 0
framework/src/main/java/com/hys/app/framework/database/impl/MySqlColumnMapRowMapper.java

@@ -0,0 +1,39 @@
+package com.hys.app.framework.database.impl;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.springframework.jdbc.core.ColumnMapRowMapper;
+import org.springframework.jdbc.support.JdbcUtils;
+
+/**
+ * map mapper
+ * @author kingapex
+ * @version v1.0
+ * @since v7.0.0
+ * 2018年3月23日 上午10:26:41
+ */
+public class MySqlColumnMapRowMapper extends ColumnMapRowMapper {
+	@Override
+	public Map<String, Object>  mapRow(ResultSet rs, int rowNum) throws SQLException {
+		ResultSetMetaData rsmd = rs.getMetaData();
+		int columnCount = rsmd.getColumnCount();
+		Map mapOfColValues = createColumnMap(columnCount);
+		for (int i = 1; i <= columnCount; i++) {
+			String key = getColumnKey(JdbcUtils.lookupColumnName(rsmd, i));
+			key = key.toLowerCase();
+			Object obj = null;
+			String typename= rsmd.getColumnTypeName(i).toUpperCase();
+			if("DECIMAL".equals(typename)){
+				obj = rs.getDouble(i);
+			}else{
+				obj = getColumnValue(rs, i);
+			}
+			 
+			mapOfColValues.put(key, obj);
+		}
+		return mapOfColValues;
+	}
+}

+ 126 - 0
framework/src/main/java/com/hys/app/framework/database/impl/MySqlMetaBuilderImpl.java

@@ -0,0 +1,126 @@
+package com.hys.app.framework.database.impl;
+
+import com.hys.app.framework.database.ColumnMeta;
+import com.hys.app.framework.database.DataMeta;
+import com.hys.app.framework.database.SqlMetaBuilder;
+import com.hys.app.framework.sncreator.SnCreator;
+import com.hys.app.framework.util.ReflectionUtil;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 基于Mysql的基本增删改查操作实现类
+ *
+ * @author Snow create in 2018/3/21
+ * @version v2.0
+ * @since v7.0.0
+ */
+@Service
+public class MySqlMetaBuilderImpl implements SqlMetaBuilder {
+
+    @Autowired
+    SnCreator snCreator;
+
+
+    @Override
+    public <T> DataMeta insert(T model) {
+
+        ColumnMeta columnMeta = ReflectionUtil.getColumnMeta(model);
+        String primaryKeyName = columnMeta.getPrimaryKeyName();
+        Long idValue = snCreator.create(1);
+
+        Object[] columnName = columnMeta.getNames();
+        Object[] columnValue = columnMeta.getValues();
+
+        //拼接主键
+        columnName = arrayInsert(columnName, primaryKeyName);
+        columnValue = arrayInsert(columnValue, idValue);
+
+        StringBuffer questionMarkStr = new StringBuffer();
+        for (int i = 0; i < columnValue.length; i++) {
+            questionMarkStr.append("?");
+            if ((i + 1) != columnValue.length) {
+                questionMarkStr.append(",");
+            }
+        }
+
+
+        //修改使其能获取父类Table注解的表名 add by liuyulei 2019-12-12
+        String tableName = ReflectionUtil.getTableName(model.getClass());
+        String columnNameStr = StringUtils.join(columnName, ",");
+
+        String addSql = "INSERT INTO " + tableName + " (" + columnNameStr + ") VALUES (" + questionMarkStr.toString() + ")";
+
+        DataMeta dataMeta = new DataMeta();
+        dataMeta.setPrimaryValue(idValue);
+
+        dataMeta.setSql(addSql);
+        dataMeta.setParamter(columnValue);
+        return dataMeta;
+    }
+
+    @Override
+    public <T> DataMeta update(T model, Long id) {
+        ColumnMeta columnMeta = ReflectionUtil.getColumnMeta(model);
+        Object[] columnName = columnMeta.getNames();
+        Object[] columnValue = columnMeta.getValues();
+
+        String columnId = ReflectionUtil.getPrimaryKey(model.getClass());
+        //修改使其能获取父类Table注解的表名 add by liuyulei 2019-12-12
+        String tableName = ReflectionUtil.getTableName(model.getClass());
+
+        List valueList = new ArrayList();
+        StringBuffer setStr = new StringBuffer();
+        for (int i = 0; i < columnName.length; i++) {
+            setStr.append(columnName[i] + "=?");
+            valueList.add(columnValue[i]);
+            if ((i + 1) != columnName.length) {
+                setStr.append(",");
+            }
+        }
+        String editSql = "UPDATE " + tableName + " SET " + setStr.toString() + " WHERE " + columnId + "=?";
+        valueList.add(id);
+
+        DataMeta dataMeta = new DataMeta();
+        dataMeta.setSql(editSql);
+        dataMeta.setParamter(valueList.toArray());
+        return dataMeta;
+    }
+
+
+    @Override
+    public String queryForModel(Class clazz) {
+        //修改使其能获取父类Table注解的表名 add by liuyulei 2019-12-12
+        String tableName = ReflectionUtil.getTableName(clazz);
+        String columnId = ReflectionUtil.getPrimaryKey(clazz);
+        String queryOneSql = "SELECT * FROM " + tableName + " WHERE " + columnId + "=?";
+        return queryOneSql;
+    }
+
+    @Override
+    public String delete(Class clazz) {
+        //修改使其能获取父类Table注解的表名 add by liuyulei 2019-12-12
+        String tableName = ReflectionUtil.getTableName(clazz);
+        String columnId = ReflectionUtil.getPrimaryKey(clazz);
+        String deleteSql = "DELETE FROM " + tableName + " WHERE " + columnId + "=?";
+        return deleteSql;
+    }
+
+    /**
+     * 给数组的最后添加元素
+     *
+     * @param ar
+     * @param value
+     * @return
+     */
+    Object[] arrayInsert(Object[] ar, Object value) {
+        ar = Arrays.copyOf(ar, ar.length + 1);
+        ar[ar.length - 1] = value;
+        return ar;
+    }
+}

+ 52 - 0
framework/src/main/java/com/hys/app/framework/database/impl/OracleColumnMapRowMapper.java

@@ -0,0 +1,52 @@
+package com.hys.app.framework.database.impl;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.springframework.jdbc.core.ColumnMapRowMapper;
+import org.springframework.jdbc.support.JdbcUtils;
+
+/**
+ * 小写key的columnrowmapper
+ *  本类覆写了spring 的ColumnMapRowMapper,将字段名全部小写
+ * @author kingapex
+ * 2010-6-13上午11:01:34
+ */
+public class OracleColumnMapRowMapper extends ColumnMapRowMapper {
+
+	@Override
+	public Map<String, Object>  mapRow(ResultSet rs, int rowNum) throws SQLException {
+		ResultSetMetaData rsmd = rs.getMetaData();
+		int columnCount = rsmd.getColumnCount();
+		Map mapOfColValues = createColumnMap(columnCount);
+		for (int i = 1; i <= columnCount; i++) {
+			String key = getColumnKey(JdbcUtils.lookupColumnName(rsmd, i));
+			key = key.toLowerCase();
+			Object obj = null;
+			String typename= rsmd.getColumnTypeName(i);
+			if("NUMBER".equals(typename)){
+				int scale = rsmd.getScale(i);
+				int precision = rsmd.getPrecision(i);
+				if(scale == 0){
+					if(precision<=10) {
+						obj = rs.getInt(i);
+					}
+					else {
+						obj = rs.getLong(i);
+					}
+				}else if(scale>0){
+					obj = rs.getDouble(i);
+				}else {
+					obj = rs.getLong(i);
+				}
+			}else{
+				obj = getColumnValue(rs, i);
+			}
+ 
+			mapOfColValues.put(key, obj);
+		}
+		return mapOfColValues;
+	}
+}

+ 42 - 0
framework/src/main/java/com/hys/app/framework/database/impl/SqlPaser.java

@@ -0,0 +1,42 @@
+package com.hys.app.framework.database.impl;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * sql解析器
+ * @author kingapex
+ *
+ */
+public class SqlPaser {
+	
+	
+	public static String insertSelectField(String field,String sql){
+		sql = "select " + field+","+sql.substring(6, sql.length());
+		return sql;
+	}
+	
+	
+	/**
+	 * 从一个sql语句中找到order by 子句
+	 * @param sql
+	 * @return
+	 */
+	public static String findOrderStr(String sql ){
+
+		String pattern = "(order\\s*by[\\w|\\W|\\s|\\S]*)";
+		Pattern p = Pattern.compile(pattern, 2 | Pattern.DOTALL);
+		Matcher m = p.matcher(sql);
+
+		if (m.find()) {
+			return m.group();
+		 
+		} 
+		return null;
+	}
+	
+	
+	public static void main(String[] args){
+		String sql ="select * from abc where 12=12 order by id asc ";
+	}
+}

+ 64 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/DefaultDBFieldHandler.java

@@ -0,0 +1,64 @@
+package com.hys.app.framework.database.mybatisplus;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.hys.app.framework.context.user.AdminUserContext;
+import com.hys.app.framework.database.mybatisplus.base.BaseDO;
+import com.hys.app.framework.util.DateUtil;
+import org.apache.ibatis.reflection.MetaObject;
+
+import java.util.Objects;
+
+/**
+ * 通用参数填充实现类
+ * 如果没有显式的对通用参数进行赋值,这里会对通用参数进行填充、赋值
+ */
+public class DefaultDBFieldHandler implements MetaObjectHandler {
+
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) {
+            BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();
+
+            // 创建时间为空,则以当前时间为插入时间
+            if (Objects.isNull(baseDO.getCreateTime())) {
+                baseDO.setCreateTime(DateUtil.getDateline());
+            }
+            // 更新时间为空,则以当前时间为更新时间
+            if (Objects.isNull(baseDO.getUpdateTime())) {
+                baseDO.setUpdateTime(DateUtil.getDateline());
+            }
+
+            String adminUserName = AdminUserContext.getAdminUserName();
+            Long companyId = AdminUserContext.getCompanyId();
+
+            // 当前登录用户不为空,创建人为空,则当前登录用户为创建人
+            if (Objects.nonNull(adminUserName) && Objects.isNull(baseDO.getCreator())) {
+                baseDO.setCreator(adminUserName);
+            }
+            // 当前登录用户不为空,更新人为空,则当前登录用户为更新人
+            if (Objects.nonNull(adminUserName) && Objects.isNull(baseDO.getUpdater())) {
+                baseDO.setUpdater(adminUserName);
+            }
+            // 当前登录用户不为空,更新人为空,则当前登录用户为更新人
+            if (Objects.nonNull(companyId) && Objects.isNull(baseDO.getCompanyId())) {
+                baseDO.setCompanyId(companyId);
+            }
+        }
+    }
+
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        // 更新时间为空,则以当前时间为更新时间
+        Object modifyTime = getFieldValByName("updateTime", metaObject);
+        if (Objects.isNull(modifyTime)) {
+            setFieldValByName("updateTime", DateUtil.getDateline(), metaObject);
+        }
+
+        // 当前登录用户不为空,更新人为空,则当前登录用户为更新人
+        Object modifier = getFieldValByName("updater", metaObject);
+        String adminUserName = AdminUserContext.getAdminUserName();
+        if (Objects.nonNull(adminUserName) && Objects.isNull(modifier)) {
+            setFieldValByName("updater", adminUserName, metaObject);
+        }
+    }
+}

+ 18 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/MybatisAutoConfiguration.java

@@ -0,0 +1,18 @@
+package com.hys.app.framework.database.mybatisplus;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * MyBaits Plus配置类
+ */
+@Configuration
+public class MybatisAutoConfiguration {
+
+    @Bean
+    public MetaObjectHandler defaultMetaObjectHandler() {
+        return new DefaultDBFieldHandler(); // 自动填充参数类
+    }
+
+}

+ 46 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/base/BaseDO.java

@@ -0,0 +1,46 @@
+package com.hys.app.framework.database.mybatisplus.base;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Data;
+import org.apache.ibatis.type.JdbcType;
+
+import java.io.Serializable;
+
+/**
+ * 基础实体对象
+ *
+ * @author 张崧
+ * @since 2023-11-28
+ */
+@Data
+public abstract class BaseDO implements Serializable {
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Long createTime;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Long companyId;
+    /**
+     * 最后更新时间
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateTime;
+    /**
+     * 创建者
+     */
+    @TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)
+    private String creator;
+    /**
+     * 更新者
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR)
+    private String updater;
+
+}

+ 157 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/base/BaseMapperX.java

@@ -0,0 +1,157 @@
+package com.hys.app.framework.database.mybatisplus.base;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
+import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
+import com.github.yulichang.interfaces.MPJBaseJoin;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import com.hys.app.framework.database.WebPage;
+import com.hys.app.framework.database.mybatisplus.wrapper.LambdaQueryChainWrapperX;
+import com.hys.app.framework.database.mybatisplus.wrapper.QueryChainWrapperX;
+import com.hys.app.framework.util.PageConvert;
+import com.hys.app.framework.database.BaseQueryParam;
+import com.github.yulichang.base.MPJBaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Collection;
+import java.util.List;
+
+import static com.hys.app.framework.database.BaseQueryParam.PAGE_NONE;
+
+/**
+ * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
+ *
+ * 1. {@link BaseMapper} 为 MyBatis Plus 的基础接口,提供基础的 CRUD 能力
+ * 2. {@link MPJBaseMapper} 为 MyBatis Plus Join 的基础接口,提供连表 Join 能力
+ */
+public interface BaseMapperX<T> extends MPJBaseMapper<T> {
+
+
+    /**
+     * 分页查询
+     * @param pageParam 分页查询参数
+     * @return 分页数据
+     */
+    default WebPage<T> selectPage(BaseQueryParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
+        // 如果不分页,直接查列表
+        if(pageParam.getPageSize() == PAGE_NONE){
+            List<T> list = selectList(queryWrapper);
+            return new WebPage<>(pageParam.getPageNo(), (long) list.size(), pageParam.getPageSize(), list);
+        }
+        IPage<T> mpPage = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
+
+        selectPage(mpPage, queryWrapper);
+
+        return PageConvert.convert(mpPage);
+    }
+
+    default QueryChainWrapperX<T> query() {
+        return new QueryChainWrapperX<>(this);
+    }
+
+    default LambdaQueryChainWrapperX<T> lambdaQuery() {
+        return new LambdaQueryChainWrapperX<>(this);
+    }
+
+    default UpdateChainWrapper<T> update() {
+        return ChainWrappers.updateChain(this);
+    }
+
+    default LambdaUpdateChainWrapper<T> lambdaUpdate() {
+        return ChainWrappers.lambdaUpdateChain(this);
+    }
+
+    default T selectOne(String field, Object value) {
+        return selectOne(new QueryWrapper<T>().eq(field, value));
+    }
+
+    default T selectOne(SFunction<T, ?> field, Object value) {
+        return selectOne(new LambdaQueryWrapper<T>().eq(field, value));
+    }
+
+    default T selectOne(String field1, Object value1, String field2, Object value2) {
+        return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2));
+    }
+
+    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
+        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
+    }
+
+    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
+                        SFunction<T, ?> field3, Object value3) {
+        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
+                .eq(field3, value3));
+    }
+
+    default Long selectCount() {
+        return selectCount(new QueryWrapper<>());
+    }
+
+    default Long selectCount(String field, Object value) {
+        return selectCount(new QueryWrapper<T>().eq(field, value));
+    }
+
+    default Long selectCount(SFunction<T, ?> field, Object value) {
+        return selectCount(new LambdaQueryWrapper<T>().eq(field, value));
+    }
+
+    default List<T> selectList() {
+        return selectList(new QueryWrapper<>());
+    }
+
+    default List<T> selectList(String field, Object value) {
+        return selectList(new QueryWrapper<T>().eq(field, value));
+    }
+
+    default List<T> selectList(SFunction<T, ?> field, Object value) {
+        return selectList(new LambdaQueryWrapper<T>().eq(field, value));
+    }
+
+    default List<T> selectList(String field, Collection<?> values) {
+        if (CollUtil.isEmpty(values)) {
+            return CollUtil.newArrayList();
+        }
+        return selectList(new QueryWrapper<T>().in(field, values));
+    }
+
+    default List<T> selectList(SFunction<T, ?> field, Collection<?> values) {
+        if (CollUtil.isEmpty(values)) {
+            return CollUtil.newArrayList();
+        }
+        return selectList(new LambdaQueryWrapper<T>().in(field, values));
+    }
+
+    default List<T> selectList(SFunction<T, ?> leField, SFunction<T, ?> geField, Object value) {
+        return selectList(new LambdaQueryWrapper<T>().le(leField, value).ge(geField, value));
+    }
+
+    default <D> WebPage<D> selectJoinPage(BaseQueryParam pageParam, Class<D> clazz, MPJLambdaWrapper<T> lambdaWrapper) {
+        // 特殊:不分页,直接查询全部
+        if(pageParam.getPageSize() == PAGE_NONE){
+            List<D> list = selectJoinList(clazz, lambdaWrapper);
+            return new WebPage<>(pageParam.getPageNo(), (long) list.size(), pageParam.getPageSize(), list);
+        }
+
+        // MyBatis Plus Join 查询
+        IPage<D> mpPage = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
+        mpPage = selectJoinPage(mpPage, clazz, lambdaWrapper);
+        // 转换返回
+        return PageConvert.convert(mpPage);
+    }
+
+    default <DTO> WebPage<DTO> selectJoinPage(BaseQueryParam pageParam, Class<DTO> resultTypeClass, MPJBaseJoin<T> joinQueryWrapper) {
+        IPage<DTO> mpPage = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
+        selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper);
+        // 转换返回
+        return PageConvert.convert(mpPage);
+    }
+
+}

+ 72 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/base/BaseService.java

@@ -0,0 +1,72 @@
+package com.hys.app.framework.database.mybatisplus.base;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.github.yulichang.base.MPJBaseService;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * Mybatis Plus IService的拓展
+ * 1. {@link IService} 为 MyBatis Plus 的基础接口,提供基础的 CRUD 能力
+ * 2. {@link MPJBaseService} 为 MyBatis Plus Join 的基础接口,提供连表 Join 能力
+ *
+ * @author 张崧
+ */
+public interface BaseService<T> extends MPJBaseService<T> {
+
+    /**
+     * 根据id集合查询
+     *
+     * @param ids id集合
+     * @return 集合
+     */
+    @Override
+    default List<T> listByIds(Collection<? extends Serializable> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        return this.getBaseMapper().selectBatchIds(ids);
+    }
+
+    /**
+     * 根据某个字段in查询,并转为以该字段为key,数据库实体为value的map
+     * eg:根据用户编号集合查询出多个用户,返回以用户编号为key,用户实体为value的map
+     *
+     * @param inValue  查询的字段值
+     * @param keyField 查询的字段 & map的key
+     * @return Map集合
+     */
+    default <K> Map<K, T> listAndConvertMap(Collection<?> inValue, SFunction<T, K> keyField) {
+        return listAndConvertMap(inValue, keyField, Function.identity());
+    }
+
+    /**
+     * 根据某个字段in查询,并转为以该字段为key,指定字段为value的map
+     * eg:根据用户编号集合查询出多个用户,返回以用户编号为key,用户名为value的map
+     *
+     * @param inValue    查询的字段值
+     * @param keyField   查询的字段 & map的key
+     * @param valueField map的value
+     * @return Map集合
+     */
+    default <K, V> Map<K, V> listAndConvertMap(Collection<?> inValue, SFunction<T, K> keyField, Function<T, V> valueField) {
+        if (CollUtil.isEmpty(inValue)) {
+            return Collections.emptyMap();
+        }
+
+        return lambdaQuery()
+                .in(keyField, inValue)
+                .list()
+                .stream()
+                .collect(Collectors.toMap(keyField, valueField));
+    }
+
+}

+ 16 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/base/BaseServiceImpl.java

@@ -0,0 +1,16 @@
+package com.hys.app.framework.database.mybatisplus.base;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.github.yulichang.base.MPJBaseMapper;
+import com.github.yulichang.base.MPJBaseServiceImpl;
+
+/**
+ * Mybatis Plus ServiceImpl的拓展
+ * 1. {@link ServiceImpl} 为 MyBatis Plus 的基础接口,提供基础的 CRUD 能力
+ * 2. {@link MPJBaseServiceImpl} 为 MyBatis Plus Join 的基础接口,提供连表 Join 能力
+ *
+ * @author 张崧
+ */
+public class BaseServiceImpl<M extends MPJBaseMapper<T>, T> extends MPJBaseServiceImpl<M, T> implements BaseService<T> {
+
+}

+ 56 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/type/LongListTypeHandler.java

@@ -0,0 +1,56 @@
+package com.hys.app.framework.database.mybatisplus.type;
+
+import cn.hutool.core.collection.CollUtil;
+import com.hys.app.framework.util.StringUtil;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+import org.apache.ibatis.type.TypeHandler;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * List<Long> 的类型转换器实现类,对应数据库的 varchar 类型
+ *
+ */
+@MappedJdbcTypes(JdbcType.VARCHAR)
+@MappedTypes(List.class)
+public class LongListTypeHandler implements TypeHandler<List<Long>> {
+
+    private static final String COMMA = ",";
+
+    @Override
+    public void setParameter(PreparedStatement ps, int i, List<Long> strings, JdbcType jdbcType) throws SQLException {
+        // 设置占位符
+        ps.setString(i, CollUtil.join(strings, COMMA));
+    }
+
+    @Override
+    public List<Long> getResult(ResultSet rs, String columnName) throws SQLException {
+        String value = rs.getString(columnName);
+        return getResult(value);
+    }
+
+    @Override
+    public List<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {
+        String value = rs.getString(columnIndex);
+        return getResult(value);
+    }
+
+    @Override
+    public List<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {
+        String value = cs.getString(columnIndex);
+        return getResult(value);
+    }
+
+    private List<Long> getResult(String value) {
+        if (value == null) {
+            return null;
+        }
+        return StringUtil.splitToLong(value, COMMA);
+    }
+}

+ 55 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/type/StringListTypeHandler.java

@@ -0,0 +1,55 @@
+package com.hys.app.framework.database.mybatisplus.type;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+import org.apache.ibatis.type.TypeHandler;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * List<String> 的类型转换器实现类,对应数据库的 varchar 类型
+ */
+@MappedJdbcTypes(JdbcType.VARCHAR)
+@MappedTypes(List.class)
+public class StringListTypeHandler implements TypeHandler<List<String>> {
+
+    private static final String COMMA = ",";
+
+    @Override
+    public void setParameter(PreparedStatement ps, int i, List<String> strings, JdbcType jdbcType) throws SQLException {
+        // 设置占位符
+        ps.setString(i, CollUtil.join(strings, COMMA));
+    }
+
+    @Override
+    public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
+        String value = rs.getString(columnName);
+        return getResult(value);
+    }
+
+    @Override
+    public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
+        String value = rs.getString(columnIndex);
+        return getResult(value);
+    }
+
+    @Override
+    public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
+        String value = cs.getString(columnIndex);
+        return getResult(value);
+    }
+
+    private List<String> getResult(String value) {
+        if (value == null) {
+            return null;
+        }
+        return StrUtil.splitTrim(value, COMMA);
+    }
+}

+ 180 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/LambdaQueryChainWrapperX.java

@@ -0,0 +1,180 @@
+package com.hys.app.framework.database.mybatisplus.wrapper;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.hys.app.framework.database.WebPage;
+import com.hys.app.framework.util.PageConvert;
+import com.hys.app.framework.database.BaseQueryParam;
+import com.hys.app.framework.util.ArrayUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static com.hys.app.framework.database.BaseQueryParam.PAGE_NONE;
+
+/**
+ * 拓展 MyBatis Plus LambdaQueryChainWrapper 类,主要增加如下功能:
+ * <p>
+ * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
+ *
+ * @param <T> 数据类型
+ *
+ * @author 张崧
+ * @since 2023-11-28
+ */
+public class LambdaQueryChainWrapperX<T> extends LambdaQueryChainWrapper<T> {
+
+    public LambdaQueryChainWrapperX(BaseMapper<T> baseMapper) {
+        super(baseMapper);
+    }
+
+    public LambdaQueryChainWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {
+        if (StringUtils.hasText(val)) {
+            return (LambdaQueryChainWrapperX<T>) super.like(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
+        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
+            return (LambdaQueryChainWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {
+        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
+            return (LambdaQueryChainWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {
+        if (ObjectUtil.isNotEmpty(val)) {
+            return (LambdaQueryChainWrapperX<T>) super.eq(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {
+        if (ObjectUtil.isNotEmpty(val)) {
+            return (LambdaQueryChainWrapperX<T>) super.ne(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (LambdaQueryChainWrapperX<T>) super.gt(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (LambdaQueryChainWrapperX<T>) super.ge(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (LambdaQueryChainWrapperX<T>) super.lt(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (LambdaQueryChainWrapperX<T>) super.le(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {
+        if (val1 != null && val2 != null) {
+            return (LambdaQueryChainWrapperX<T>) super.between(column, val1, val2);
+        }
+        if (val1 != null) {
+            return (LambdaQueryChainWrapperX<T>) ge(column, val1);
+        }
+        if (val2 != null) {
+            return (LambdaQueryChainWrapperX<T>) le(column, val2);
+        }
+        return this;
+    }
+
+    public LambdaQueryChainWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {
+        Object val1 = ArrayUtils.get(values, 0);
+        Object val2 = ArrayUtils.get(values, 1);
+        return betweenIfPresent(column, val1, val2);
+    }
+
+    public LambdaQueryChainWrapperX<T> andX(boolean condition, Consumer<LambdaQueryWrapper<T>> consumer) {
+        this.getWrapper().and(condition, consumer);
+        return this;
+    }
+
+    // ========== 重写父类方法,方便链式调用 ==========
+
+    @Override
+    public LambdaQueryChainWrapperX<T> eq(boolean condition, SFunction<T, ?> column, Object val) {
+        super.eq(condition, column, val);
+        return this;
+    }
+
+    @Override
+    public LambdaQueryChainWrapperX<T> eq(SFunction<T, ?> column, Object val) {
+        super.eq(column, val);
+        return this;
+    }
+
+    @Override
+    public LambdaQueryChainWrapperX<T> orderByDesc(SFunction<T, ?> column) {
+        super.orderByDesc(true, column);
+        return this;
+    }
+
+    @Override
+    public LambdaQueryChainWrapperX<T> orderByAsc(SFunction<T, ?> column) {
+        super.orderByAsc(true, column);
+        return this;
+    }
+
+    @Override
+    public LambdaQueryChainWrapperX<T> last(String lastSql) {
+        super.last(lastSql);
+        return this;
+    }
+
+    @Override
+    public LambdaQueryChainWrapperX<T> in(SFunction<T, ?> column, Collection<?> coll) {
+        super.in(column, coll);
+        return this;
+    }
+
+    /**
+     * 分页查询
+     * @param pageParam 分页查询参数
+     * @return 分页数据
+     */
+    public WebPage<T> page(BaseQueryParam pageParam) {
+        // 如果不分页,直接查列表
+        if(pageParam.getPageSize() == PAGE_NONE){
+            List<T> list = list();
+            return new WebPage<>(pageParam.getPageNo(), (long) list.size(), pageParam.getPageSize(), list);
+        }
+
+        IPage<T> mpPage = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
+        super.page(mpPage);
+        return PageConvert.convert(mpPage);
+    }
+}

+ 135 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/LambdaQueryWrapperX.java

@@ -0,0 +1,135 @@
+package com.hys.app.framework.database.mybatisplus.wrapper;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.hys.app.framework.util.ArrayUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.Collection;
+
+/**
+ * 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能:
+ * <p>
+ * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
+ *
+ * @param <T> 数据类型
+ */
+public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {
+
+    public LambdaQueryWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {
+        if (StringUtils.hasText(val)) {
+            return (LambdaQueryWrapperX<T>) super.like(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
+        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
+            return (LambdaQueryWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {
+        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
+            return (LambdaQueryWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {
+        if (ObjectUtil.isNotEmpty(val)) {
+            return (LambdaQueryWrapperX<T>) super.eq(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {
+        if (ObjectUtil.isNotEmpty(val)) {
+            return (LambdaQueryWrapperX<T>) super.ne(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (LambdaQueryWrapperX<T>) super.gt(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (LambdaQueryWrapperX<T>) super.ge(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (LambdaQueryWrapperX<T>) super.lt(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (LambdaQueryWrapperX<T>) super.le(column, val);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {
+        if (val1 != null && val2 != null) {
+            return (LambdaQueryWrapperX<T>) super.between(column, val1, val2);
+        }
+        if (val1 != null) {
+            return (LambdaQueryWrapperX<T>) ge(column, val1);
+        }
+        if (val2 != null) {
+            return (LambdaQueryWrapperX<T>) le(column, val2);
+        }
+        return this;
+    }
+
+    public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {
+        Object val1 = ArrayUtils.get(values, 0);
+        Object val2 = ArrayUtils.get(values, 1);
+        return betweenIfPresent(column, val1, val2);
+    }
+
+    // ========== 重写父类方法,方便链式调用 ==========
+
+    @Override
+    public LambdaQueryWrapperX<T> eq(boolean condition, SFunction<T, ?> column, Object val) {
+        super.eq(condition, column, val);
+        return this;
+    }
+
+    @Override
+    public LambdaQueryWrapperX<T> eq(SFunction<T, ?> column, Object val) {
+        super.eq(column, val);
+        return this;
+    }
+
+    @Override
+    public LambdaQueryWrapperX<T> orderByDesc(SFunction<T, ?> column) {
+        super.orderByDesc(true, column);
+        return this;
+    }
+
+    @Override
+    public LambdaQueryWrapperX<T> last(String lastSql) {
+        super.last(lastSql);
+        return this;
+    }
+
+    @Override
+    public LambdaQueryWrapperX<T> in(SFunction<T, ?> column, Collection<?> coll) {
+        super.in(column, coll);
+        return this;
+    }
+
+}

+ 319 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/MPJLambdaWrapperX.java

@@ -0,0 +1,319 @@
+package com.hys.app.framework.database.mybatisplus.wrapper;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.hys.app.framework.util.ArrayUtils;
+import com.github.yulichang.toolkit.MPJWrappers;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import org.springframework.util.StringUtils;
+
+import java.util.Collection;
+import java.util.function.Consumer;
+
+/**
+ * 拓展 MyBatis Plus Join QueryWrapper 类,主要增加如下功能:
+ * <p>
+ * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
+ *
+ * @param <T> 数据类型
+ */
+public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {
+
+    public MPJLambdaWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {
+        MPJWrappers.lambdaJoin().like(column, val);
+        if (StringUtils.hasText(val)) {
+            return (MPJLambdaWrapperX<T>) super.like(column, val);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
+        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
+            return (MPJLambdaWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {
+        if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
+            return (MPJLambdaWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {
+        if (ObjectUtil.isNotEmpty(val)) {
+            return (MPJLambdaWrapperX<T>) super.eq(column, val);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {
+        if (ObjectUtil.isNotEmpty(val)) {
+            return (MPJLambdaWrapperX<T>) super.ne(column, val);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (MPJLambdaWrapperX<T>) super.gt(column, val);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (MPJLambdaWrapperX<T>) super.ge(column, val);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (MPJLambdaWrapperX<T>) super.lt(column, val);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {
+        if (val != null) {
+            return (MPJLambdaWrapperX<T>) super.le(column, val);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {
+        if (val1 != null && val2 != null) {
+            return (MPJLambdaWrapperX<T>) super.between(column, val1, val2);
+        }
+        if (val1 != null) {
+            return (MPJLambdaWrapperX<T>) ge(column, val1);
+        }
+        if (val2 != null) {
+            return (MPJLambdaWrapperX<T>) le(column, val2);
+        }
+        return this;
+    }
+
+    public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {
+        Object val1 = ArrayUtils.get(values, 0);
+        Object val2 = ArrayUtils.get(values, 1);
+        return betweenIfPresent(column, val1, val2);
+    }
+
+    // ========== 重写父类方法,方便链式调用 ==========
+
+    @Override
+    public <X> MPJLambdaWrapperX<T> eq(boolean condition, SFunction<X, ?> column, Object val) {
+        super.eq(condition, column, val);
+        return this;
+    }
+
+    @Override
+    public <X> MPJLambdaWrapperX<T> eq(SFunction<X, ?> column, Object val) {
+        super.eq(column, val);
+        return this;
+    }
+
+    @Override
+    public <X> MPJLambdaWrapperX<T> orderByDesc(SFunction<X, ?> column) {
+        //noinspection unchecked
+        super.orderByDesc(true, column);
+        return this;
+    }
+
+    @Override
+    public MPJLambdaWrapperX<T> last(String lastSql) {
+        super.last(lastSql);
+        return this;
+    }
+
+    @Override
+    public <X> MPJLambdaWrapperX<T> in(SFunction<X, ?> column, Collection<?> coll) {
+        super.in(column, coll);
+        return this;
+    }
+
+    @Override
+    public MPJLambdaWrapperX<T> selectAll(Class<?> clazz) {
+        super.selectAll(clazz);
+        return this;
+    }
+
+    @Override
+    public MPJLambdaWrapperX<T> selectAll(Class<?> clazz, String prefix) {
+        super.selectAll(clazz, prefix);
+        return this;
+    }
+
+    @Override
+    public <S> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, String alias) {
+        super.selectAs(column, alias);
+        return this;
+    }
+
+    @Override
+    public <E> MPJLambdaWrapperX<T> selectAs(String column, SFunction<E, ?> alias) {
+        super.selectAs(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, SFunction<X, ?> alias) {
+        super.selectAs(column, alias);
+        return this;
+    }
+
+    @Override
+    public <E, X> MPJLambdaWrapperX<T> selectAs(String index, SFunction<E, ?> column, SFunction<X, ?> alias) {
+        super.selectAs(index, column, alias);
+        return this;
+    }
+
+    @Override
+    public <E> MPJLambdaWrapperX<T> selectAsClass(Class<E> source, Class<?> tag) {
+        super.selectAsClass(source, tag);
+        return this;
+    }
+
+    @Override
+    public <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) {
+        super.selectSub(clazz, consumer, alias);
+        return this;
+    }
+
+    @Override
+    public <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, String st, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) {
+        super.selectSub(clazz, st, consumer, alias);
+        return this;
+    }
+
+    @Override
+    public <S> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column) {
+        super.selectCount(column);
+        return this;
+    }
+
+    @Override
+    public MPJLambdaWrapperX<T> selectCount(Object column, String alias) {
+        super.selectCount(column, alias);
+        return this;
+    }
+
+    @Override
+    public <X> MPJLambdaWrapperX<T> selectCount(Object column, SFunction<X, ?> alias) {
+        super.selectCount(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, String alias) {
+        super.selectCount(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, SFunction<X, ?> alias) {
+        super.selectCount(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column) {
+        super.selectSum(column);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, String alias) {
+        super.selectSum(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, SFunction<X, ?> alias) {
+        super.selectSum(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column) {
+        super.selectMax(column);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, String alias) {
+        super.selectMax(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, SFunction<X, ?> alias) {
+        super.selectMax(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column) {
+        super.selectMin(column);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, String alias) {
+        super.selectMin(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, SFunction<X, ?> alias) {
+        super.selectMin(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column) {
+        super.selectAvg(column);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, String alias) {
+        super.selectAvg(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, SFunction<X, ?> alias) {
+        super.selectAvg(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column) {
+        super.selectLen(column);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, String alias) {
+        super.selectLen(column, alias);
+        return this;
+    }
+
+    @Override
+    public <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, SFunction<X, ?> alias) {
+        super.selectLen(column, alias);
+        return this;
+    }
+
+    @Override
+    public <D, X> MPJLambdaWrapperX<T> leftJoin(Class<D> clazz, SFunction<D, ?> left, SFunction<X, ?> right) {
+        super.join("LEFT JOIN", clazz, left, right);
+        return this;
+    }
+
+}

+ 170 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/QueryChainWrapperX.java

@@ -0,0 +1,170 @@
+package com.hys.app.framework.database.mybatisplus.wrapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.hys.app.framework.database.WebPage;
+import com.hys.app.framework.util.PageConvert;
+import com.hys.app.framework.database.BaseQueryParam;
+import org.springframework.util.StringUtils;
+
+import java.util.Collection;
+import java.util.List;
+
+import static com.hys.app.framework.database.BaseQueryParam.PAGE_NONE;
+
+/**
+ * 拓展 MyBatis Plus QueryChainWrapper 类,主要增加如下功能:
+ *
+ * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
+ *
+ * @param <T> 数据类型
+ */
+public class QueryChainWrapperX<T> extends QueryChainWrapper<T> {
+
+    public QueryChainWrapperX(BaseMapper<T> baseMapper) {
+        super(baseMapper);
+    }
+
+    public QueryChainWrapperX<T> likeIfPresent(String column, String val) {
+        if (StringUtils.hasText(val)) {
+            return (QueryChainWrapperX<T>) super.like(column, val);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> inIfPresent(String column, Collection<?> values) {
+        if (!CollectionUtils.isEmpty(values)) {
+            return (QueryChainWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> inIfPresent(String column, Object... values) {
+        if (!ArrayUtils.isEmpty(values)) {
+            return (QueryChainWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> eqIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryChainWrapperX<T>) super.eq(column, val);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> neIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryChainWrapperX<T>) super.ne(column, val);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> gtIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryChainWrapperX<T>) super.gt(column, val);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> geIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryChainWrapperX<T>) super.ge(column, val);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> ltIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryChainWrapperX<T>) super.lt(column, val);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> leIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryChainWrapperX<T>) super.le(column, val);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> betweenIfPresent(String column, Object val1, Object val2) {
+        if (val1 != null && val2 != null) {
+            return (QueryChainWrapperX<T>) super.between(column, val1, val2);
+        }
+        if (val1 != null) {
+            return (QueryChainWrapperX<T>) ge(column, val1);
+        }
+        if (val2 != null) {
+            return (QueryChainWrapperX<T>) le(column, val2);
+        }
+        return this;
+    }
+
+    public QueryChainWrapperX<T> betweenIfPresent(String column, Object[] values) {
+        if (values!= null && values.length != 0 && values[0] != null && values[1] != null) {
+            return (QueryChainWrapperX<T>) super.between(column, values[0], values[1]);
+        }
+        if (values!= null && values.length != 0 && values[0] != null) {
+            return (QueryChainWrapperX<T>) ge(column, values[0]);
+        }
+        if (values!= null && values.length != 0 && values[1] != null) {
+            return (QueryChainWrapperX<T>) le(column, values[1]);
+        }
+        return this;
+    }
+
+    // ========== 重写父类方法,方便链式调用 ==========
+
+    @Override
+    public QueryChainWrapperX<T> eq(boolean condition, String column, Object val) {
+        super.eq(condition, column, val);
+        return this;
+    }
+
+    @Override
+    public QueryChainWrapperX<T> eq(String column, Object val) {
+        super.eq(column, val);
+        return this;
+    }
+
+    @Override
+    public QueryChainWrapperX<T> orderByDesc(String column) {
+        super.orderByDesc(true, column);
+        return this;
+    }
+
+    @Override
+    public QueryChainWrapperX<T> last(String lastSql) {
+        super.last(lastSql);
+        return this;
+    }
+
+    @Override
+    public QueryChainWrapperX<T> in(String column, Collection<?> coll) {
+        super.in(column, coll);
+        return this;
+    }
+
+    /**
+     * 分页查询
+     * @param pageParam 分页查询参数
+     * @return 分页数据
+     */
+    public WebPage<T> page(BaseQueryParam pageParam) {
+        // 如果不分页,直接查列表
+        if(pageParam.getPageSize() == PAGE_NONE){
+            List<T> list = list();
+            return new WebPage<>(pageParam.getPageNo(), (long) list.size(), pageParam.getPageSize(), list);
+        }
+
+        IPage<T> mpPage = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
+        super.page(mpPage);
+        return PageConvert.convert(mpPage);
+    }
+
+}

+ 140 - 0
framework/src/main/java/com/hys/app/framework/database/mybatisplus/wrapper/QueryWrapperX.java

@@ -0,0 +1,140 @@
+package com.hys.app.framework.database.mybatisplus.wrapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.Collection;
+
+/**
+ * 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能:
+ *
+ * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
+ *
+ * @param <T> 数据类型
+ */
+public class QueryWrapperX<T> extends QueryWrapper<T> {
+
+    public QueryWrapperX<T> likeIfPresent(String column, String val) {
+        if (StringUtils.hasText(val)) {
+            return (QueryWrapperX<T>) super.like(column, val);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> inIfPresent(String column, Collection<?> values) {
+        if (!CollectionUtils.isEmpty(values)) {
+            return (QueryWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> inIfPresent(String column, Object... values) {
+        if (!ArrayUtils.isEmpty(values)) {
+            return (QueryWrapperX<T>) super.in(column, values);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> eqIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryWrapperX<T>) super.eq(column, val);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> neIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryWrapperX<T>) super.ne(column, val);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> gtIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryWrapperX<T>) super.gt(column, val);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> geIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryWrapperX<T>) super.ge(column, val);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> ltIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryWrapperX<T>) super.lt(column, val);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> leIfPresent(String column, Object val) {
+        if (val != null) {
+            return (QueryWrapperX<T>) super.le(column, val);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> betweenIfPresent(String column, Object val1, Object val2) {
+        if (val1 != null && val2 != null) {
+            return (QueryWrapperX<T>) super.between(column, val1, val2);
+        }
+        if (val1 != null) {
+            return (QueryWrapperX<T>) ge(column, val1);
+        }
+        if (val2 != null) {
+            return (QueryWrapperX<T>) le(column, val2);
+        }
+        return this;
+    }
+
+    public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) {
+        if (values!= null && values.length != 0 && values[0] != null && values[1] != null) {
+            return (QueryWrapperX<T>) super.between(column, values[0], values[1]);
+        }
+        if (values!= null && values.length != 0 && values[0] != null) {
+            return (QueryWrapperX<T>) ge(column, values[0]);
+        }
+        if (values!= null && values.length != 0 && values[1] != null) {
+            return (QueryWrapperX<T>) le(column, values[1]);
+        }
+        return this;
+    }
+
+    // ========== 重写父类方法,方便链式调用 ==========
+
+    @Override
+    public QueryWrapperX<T> eq(boolean condition, String column, Object val) {
+        super.eq(condition, column, val);
+        return this;
+    }
+
+    @Override
+    public QueryWrapperX<T> eq(String column, Object val) {
+        super.eq(column, val);
+        return this;
+    }
+
+    @Override
+    public QueryWrapperX<T> orderByDesc(String column) {
+        super.orderByDesc(true, column);
+        return this;
+    }
+
+    @Override
+    public QueryWrapperX<T> last(String lastSql) {
+        super.last(lastSql);
+        return this;
+    }
+
+    @Override
+    public QueryWrapperX<T> in(String column, Collection<?> coll) {
+        super.in(column, coll);
+        return this;
+    }
+
+}

+ 36 - 0
framework/src/main/java/com/hys/app/framework/datapermission/annotation/DataPermission.java

@@ -0,0 +1,36 @@
+package com.hys.app.framework.datapermission.annotation;
+
+import com.hys.app.framework.datapermission.rule.DataPermissionRule;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限注解
+ * 可声明在类或者方法上,标识使用的数据权限规则
+ *
+ * @author 张崧
+ * @since 2023-12-04
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataPermission {
+
+    /**
+     * 当前类或方法是否开启数据权限
+     * 即使不添加 @DataPermission 注解,默认是开启状态
+     * 可通过设置 enable 为 false 禁用
+     */
+    boolean enable() default true;
+
+    /**
+     * 生效的数据权限规则数组,优先级高于 {@link #excludeRules()}
+     */
+    Class<? extends DataPermissionRule>[] includeRules() default {};
+
+    /**
+     * 排除的数据权限规则数组,优先级最低
+     */
+    Class<? extends DataPermissionRule>[] excludeRules() default {};
+
+}

+ 36 - 0
framework/src/main/java/com/hys/app/framework/datapermission/aop/DataPermissionAnnotationAdvisor.java

@@ -0,0 +1,36 @@
+package com.hys.app.framework.datapermission.aop;
+
+import com.hys.app.framework.datapermission.annotation.DataPermission;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.aopalliance.aop.Advice;
+import org.springframework.aop.Pointcut;
+import org.springframework.aop.support.AbstractPointcutAdvisor;
+import org.springframework.aop.support.ComposablePointcut;
+import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
+
+/**
+ * {@link DataPermission} 注解的 Advisor 实现类
+ *
+ * @author 张崧
+ */
+@Getter
+@EqualsAndHashCode(callSuper = true)
+public class DataPermissionAnnotationAdvisor extends AbstractPointcutAdvisor {
+
+    private final Advice advice;
+
+    private final Pointcut pointcut;
+
+    public DataPermissionAnnotationAdvisor() {
+        this.advice = new DataPermissionAnnotationInterceptor();
+        this.pointcut = this.buildPointcut();
+    }
+
+    protected Pointcut buildPointcut() {
+        Pointcut classPointcut = new AnnotationMatchingPointcut(DataPermission.class, true);
+        Pointcut methodPointcut = new AnnotationMatchingPointcut(null, DataPermission.class, true);
+        return new ComposablePointcut(classPointcut).union(methodPointcut);
+    }
+
+}

+ 73 - 0
framework/src/main/java/com/hys/app/framework/datapermission/aop/DataPermissionAnnotationInterceptor.java

@@ -0,0 +1,73 @@
+package com.hys.app.framework.datapermission.aop;
+
+import com.hys.app.framework.datapermission.annotation.DataPermission;
+import lombok.Getter;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.core.MethodClassKey;
+import org.springframework.core.annotation.AnnotationUtils;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * {@link DataPermission} 注解的拦截器
+ * 1. 在执行方法前,将 @DataPermission 注解入栈
+ * 2. 在执行方法后,将 @DataPermission 注解出栈
+ *
+ * @author 张崧
+ * @since 2023-12-04
+ */
+@DataPermission // 该注解,用于 {@link DATA_PERMISSION_NULL} 的空对象
+public class DataPermissionAnnotationInterceptor implements MethodInterceptor {
+
+    /**
+     * DataPermission 空对象,用于方法无 {@link DataPermission} 注解时,使用 DATA_PERMISSION_NULL 进行占位
+     */
+    static final DataPermission DATA_PERMISSION_NULL = DataPermissionAnnotationInterceptor.class.getAnnotation(DataPermission.class);
+
+    @Getter
+    private final Map<MethodClassKey, DataPermission> dataPermissionCache = new ConcurrentHashMap<>();
+
+    @Override
+    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
+        // 入栈
+        DataPermission dataPermission = this.findAnnotation(methodInvocation);
+        if (dataPermission != null) {
+            DataPermissionContextHolder.add(dataPermission);
+        }
+        try {
+            // 执行逻辑
+            return methodInvocation.proceed();
+        } finally {
+            // 出栈
+            if (dataPermission != null) {
+                DataPermissionContextHolder.remove();
+            }
+        }
+    }
+
+    private DataPermission findAnnotation(MethodInvocation methodInvocation) {
+        // 1. 从缓存中获取
+        Method method = methodInvocation.getMethod();
+        Object targetObject = methodInvocation.getThis();
+        Class<?> clazz = targetObject != null ? targetObject.getClass() : method.getDeclaringClass();
+        MethodClassKey methodClassKey = new MethodClassKey(method, clazz);
+        DataPermission dataPermission = dataPermissionCache.get(methodClassKey);
+        if (dataPermission != null) {
+            return dataPermission != DATA_PERMISSION_NULL ? dataPermission : null;
+        }
+
+        // 2.1 从方法中获取
+        dataPermission = AnnotationUtils.findAnnotation(method, DataPermission.class);
+        // 2.2 从类上获取
+        if (dataPermission == null) {
+            dataPermission = AnnotationUtils.findAnnotation(clazz, DataPermission.class);
+        }
+        // 2.3 添加到缓存中
+        dataPermissionCache.put(methodClassKey, dataPermission != null ? dataPermission : DATA_PERMISSION_NULL);
+        return dataPermission;
+    }
+
+}

+ 65 - 0
framework/src/main/java/com/hys/app/framework/datapermission/aop/DataPermissionContextHolder.java

@@ -0,0 +1,65 @@
+package com.hys.app.framework.datapermission.aop;
+
+
+import com.alibaba.ttl.TransmittableThreadLocal;
+import com.hys.app.framework.datapermission.annotation.DataPermission;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * {@link DataPermission} 注解的 Context 上下文
+ *
+ * @author 张崧
+ * @since 2023-12-04
+ */
+public class DataPermissionContextHolder {
+
+    /**
+     * 使用 List 的原因,可能存在方法的嵌套调用
+     */
+    private static final ThreadLocal<LinkedList<DataPermission>> DATA_PERMISSIONS =
+            TransmittableThreadLocal.withInitial(LinkedList::new);
+
+    /**
+     * 获得当前的 DataPermission 注解
+     *
+     * @return DataPermission 注解
+     */
+    public static DataPermission get() {
+        return DATA_PERMISSIONS.get().peekLast();
+    }
+
+    /**
+     * 入栈 DataPermission 注解
+     *
+     * @param dataPermission DataPermission 注解
+     */
+    public static void add(DataPermission dataPermission) {
+        DATA_PERMISSIONS.get().addLast(dataPermission);
+    }
+
+    /**
+     * 出栈 DataPermission 注解
+     *
+     * @return DataPermission 注解
+     */
+    public static DataPermission remove() {
+        DataPermission dataPermission = DATA_PERMISSIONS.get().removeLast();
+        // 无元素时,清空 ThreadLocal
+        if (DATA_PERMISSIONS.get().isEmpty()) {
+            DATA_PERMISSIONS.remove();
+        }
+        return dataPermission;
+    }
+
+    /**
+     * 获得所有 DataPermission
+     *
+     * @return DataPermission 队列
+     */
+    public static List<DataPermission> getAll() {
+        return DATA_PERMISSIONS.get();
+    }
+
+}

+ 44 - 0
framework/src/main/java/com/hys/app/framework/datapermission/config/DataPermissionConfiguration.java

@@ -0,0 +1,44 @@
+package com.hys.app.framework.datapermission.config;
+
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.hys.app.framework.datapermission.db.DataPermissionDatabaseInterceptor;
+import com.hys.app.framework.datapermission.aop.DataPermissionAnnotationAdvisor;
+import com.hys.app.framework.datapermission.rule.DataPermissionRule;
+import com.hys.app.framework.datapermission.rule.DataPermissionRuleFactory;
+import com.hys.app.framework.datapermission.rule.DataPermissionRuleFactoryImpl;
+import com.hys.app.framework.util.MyBatisPlusUtils;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * 数据权限的自动配置类
+ *
+ * @author 张崧
+ */
+@Configuration
+public class DataPermissionConfiguration {
+
+    @Bean
+    public DataPermissionRuleFactory dataPermissionRuleFactory(List<DataPermissionRule> rules) {
+        return new DataPermissionRuleFactoryImpl(rules);
+    }
+
+    @Bean
+    public DataPermissionDatabaseInterceptor dataPermissionDatabaseInterceptor(MybatisPlusInterceptor interceptor,
+                                                                               DataPermissionRuleFactory ruleFactory) {
+        // 创建 DataPermissionDatabaseInterceptor 拦截器
+        DataPermissionDatabaseInterceptor inner = new DataPermissionDatabaseInterceptor(ruleFactory);
+        // 添加到 interceptor 中
+        // 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定
+        MyBatisPlusUtils.addInterceptor(interceptor, inner, 0);
+        return inner;
+    }
+
+    @Bean
+    public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() {
+        return new DataPermissionAnnotationAdvisor();
+    }
+
+}

+ 640 - 0
framework/src/main/java/com/hys/app/framework/datapermission/db/DataPermissionDatabaseInterceptor.java

@@ -0,0 +1,640 @@
+package com.hys.app.framework.datapermission.db;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
+import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
+import com.hys.app.framework.datapermission.rule.DataPermissionRule;
+import com.hys.app.framework.datapermission.rule.DataPermissionRuleFactory;
+import com.hys.app.framework.util.MyBatisPlusUtils;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.sf.jsqlparser.expression.*;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
+import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.expression.operators.relational.InExpression;
+import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.statement.delete.Delete;
+import net.sf.jsqlparser.statement.select.*;
+import net.sf.jsqlparser.statement.update.Update;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+
+import java.sql.Connection;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 数据权限拦截器,通过 {@link DataPermissionRule} 数据权限规则,重写 SQL 的方式来实现
+ * 主要的 SQL 重写方法,可见 {@link #builderExpression(Expression, List)} 方法
+ * 
+ * 代码参考 {@link com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor} 。
+ * 
+ * @author 张崧
+ * @since 2023-12-04
+ */
+@RequiredArgsConstructor
+public class DataPermissionDatabaseInterceptor extends JsqlParserSupport implements InnerInterceptor {
+
+    private final DataPermissionRuleFactory ruleFactory;
+
+    @Getter
+    private final MappedStatementCache mappedStatementCache = new MappedStatementCache();
+
+    @Override // SELECT 场景
+    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
+        // 获得 Mapper 对应的数据权限的规则
+        List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(ms.getId());
+        if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过
+            return;
+        }
+
+        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
+        try {
+            // 初始化上下文
+            ContextHolder.init(rules);
+            // 处理 SQL
+            mpBs.sql(parserSingle(mpBs.sql(), null));
+        } finally {
+            // 添加是否需要重写的缓存
+            addMappedStatementCache(ms);
+            // 清空上下文
+            ContextHolder.clear();
+        }
+    }
+
+    @Override // 只处理 UPDATE / DELETE 场景,不处理 INSERT 场景(因为 INSERT 不需要数据权限)
+    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
+        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
+        MappedStatement ms = mpSh.mappedStatement();
+        SqlCommandType sct = ms.getSqlCommandType();
+        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
+            // 获得 Mapper 对应的数据权限的规则
+            List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(ms.getId());
+            if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过
+                return;
+            }
+
+            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
+            try {
+                // 初始化上下文
+                ContextHolder.init(rules);
+                // 处理 SQL
+                mpBs.sql(parserMulti(mpBs.sql(), null));
+            } finally {
+                // 添加是否需要重写的缓存
+                addMappedStatementCache(ms);
+                // 清空上下文
+                ContextHolder.clear();
+            }
+        }
+    }
+
+    @Override
+    protected void processSelect(Select select, int index, String sql, Object obj) {
+        processSelectBody(select.getSelectBody());
+        List<WithItem> withItemsList = select.getWithItemsList();
+        if (!CollectionUtils.isEmpty(withItemsList)) {
+            withItemsList.forEach(this::processSelectBody);
+        }
+    }
+
+    /**
+     * update 语句处理
+     */
+    @Override
+    protected void processUpdate(Update update, int index, String sql, Object obj) {
+        final Table table = update.getTable();
+        update.setWhere(this.builderExpression(update.getWhere(), table));
+    }
+
+    /**
+     * delete 语句处理
+     */
+    @Override
+    protected void processDelete(Delete delete, int index, String sql, Object obj) {
+        delete.setWhere(this.builderExpression(delete.getWhere(), delete.getTable()));
+    }
+
+    // ========== 和 TenantLineInnerInterceptor 一致的逻辑 ==========
+
+    protected void processSelectBody(SelectBody selectBody) {
+        if (selectBody == null) {
+            return;
+        }
+        if (selectBody instanceof PlainSelect) {
+            processPlainSelect((PlainSelect) selectBody);
+        } else if (selectBody instanceof WithItem) {
+            WithItem withItem = (WithItem) selectBody;
+            processSelectBody(withItem.getSubSelect().getSelectBody());
+        } else {
+            SetOperationList operationList = (SetOperationList) selectBody;
+            List<SelectBody> selectBodyList = operationList.getSelects();
+            if (CollectionUtils.isNotEmpty(selectBodyList)) {
+                selectBodyList.forEach(this::processSelectBody);
+            }
+        }
+    }
+
+    /**
+     * 处理 PlainSelect
+     */
+    protected void processPlainSelect(PlainSelect plainSelect) {
+        //#3087 github
+        List<SelectItem> selectItems = plainSelect.getSelectItems();
+        if (CollectionUtils.isNotEmpty(selectItems)) {
+            selectItems.forEach(this::processSelectItem);
+        }
+
+        // 处理 where 中的子查询
+        Expression where = plainSelect.getWhere();
+        processWhereSubSelect(where);
+
+        // 处理 fromItem
+        FromItem fromItem = plainSelect.getFromItem();
+        List<Table> list = processFromItem(fromItem);
+        List<Table> mainTables = new ArrayList<>(list);
+
+        // 处理 join
+        List<Join> joins = plainSelect.getJoins();
+        if (CollectionUtils.isNotEmpty(joins)) {
+            mainTables = processJoins(mainTables, joins);
+        }
+
+        // 当有 mainTable 时,进行 where 条件追加
+        if (CollectionUtils.isNotEmpty(mainTables)) {
+            plainSelect.setWhere(builderExpression(where, mainTables));
+        }
+    }
+
+    private List<Table> processFromItem(FromItem fromItem) {
+        // 处理括号括起来的表达式
+        while (fromItem instanceof ParenthesisFromItem) {
+            fromItem = ((ParenthesisFromItem) fromItem).getFromItem();
+        }
+
+        List<Table> mainTables = new ArrayList<>();
+        // 无 join 时的处理逻辑
+        if (fromItem instanceof Table) {
+            Table fromTable = (Table) fromItem;
+            mainTables.add(fromTable);
+        } else if (fromItem instanceof SubJoin) {
+            // SubJoin 类型则还需要添加上 where 条件
+            List<Table> tables = processSubJoin((SubJoin) fromItem);
+            mainTables.addAll(tables);
+        } else {
+            // 处理下 fromItem
+            processOtherFromItem(fromItem);
+        }
+        return mainTables;
+    }
+
+    /**
+     * 处理where条件内的子查询
+     * <p>
+     * 支持如下:
+     * 1. in
+     * 2. =
+     * 3. >
+     * 4. <
+     * 5. >=
+     * 6. <=
+     * 7. <>
+     * 8. EXISTS
+     * 9. NOT EXISTS
+     * <p>
+     * 前提条件:
+     * 1. 子查询必须放在小括号中
+     * 2. 子查询一般放在比较操作符的右边
+     *
+     * @param where where 条件
+     */
+    protected void processWhereSubSelect(Expression where) {
+        if (where == null) {
+            return;
+        }
+        if (where instanceof FromItem) {
+            processOtherFromItem((FromItem) where);
+            return;
+        }
+        if (where.toString().indexOf("SELECT") > 0) {
+            // 有子查询
+            if (where instanceof BinaryExpression) {
+                // 比较符号 , and , or , 等等
+                BinaryExpression expression = (BinaryExpression) where;
+                processWhereSubSelect(expression.getLeftExpression());
+                processWhereSubSelect(expression.getRightExpression());
+            } else if (where instanceof InExpression) {
+                // in
+                InExpression expression = (InExpression) where;
+                Expression inExpression = expression.getRightExpression();
+                if (inExpression instanceof SubSelect) {
+                    processSelectBody(((SubSelect) inExpression).getSelectBody());
+                }
+            } else if (where instanceof ExistsExpression) {
+                // exists
+                ExistsExpression expression = (ExistsExpression) where;
+                processWhereSubSelect(expression.getRightExpression());
+            } else if (where instanceof NotExpression) {
+                // not exists
+                NotExpression expression = (NotExpression) where;
+                processWhereSubSelect(expression.getExpression());
+            } else if (where instanceof Parenthesis) {
+                Parenthesis expression = (Parenthesis) where;
+                processWhereSubSelect(expression.getExpression());
+            }
+        }
+    }
+
+    protected void processSelectItem(SelectItem selectItem) {
+        if (selectItem instanceof SelectExpressionItem) {
+            SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
+            if (selectExpressionItem.getExpression() instanceof SubSelect) {
+                processSelectBody(((SubSelect) selectExpressionItem.getExpression()).getSelectBody());
+            } else if (selectExpressionItem.getExpression() instanceof Function) {
+                processFunction((Function) selectExpressionItem.getExpression());
+            }
+        }
+    }
+
+    /**
+     * 处理函数
+     * <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>
+     * <p> fixed gitee pulls/141</p>
+     *
+     * @param function
+     */
+    protected void processFunction(Function function) {
+        ExpressionList parameters = function.getParameters();
+        if (parameters != null) {
+            parameters.getExpressions().forEach(expression -> {
+                if (expression instanceof SubSelect) {
+                    processSelectBody(((SubSelect) expression).getSelectBody());
+                } else if (expression instanceof Function) {
+                    processFunction((Function) expression);
+                }
+            });
+        }
+    }
+
+    /**
+     * 处理子查询等
+     */
+    protected void processOtherFromItem(FromItem fromItem) {
+        // 去除括号
+        while (fromItem instanceof ParenthesisFromItem) {
+            fromItem = ((ParenthesisFromItem) fromItem).getFromItem();
+        }
+
+        if (fromItem instanceof SubSelect) {
+            SubSelect subSelect = (SubSelect) fromItem;
+            if (subSelect.getSelectBody() != null) {
+                processSelectBody(subSelect.getSelectBody());
+            }
+        } else if (fromItem instanceof ValuesList) {
+            logger.debug("Perform a subQuery, if you do not give us feedback");
+        } else if (fromItem instanceof LateralSubSelect) {
+            LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem;
+            if (lateralSubSelect.getSubSelect() != null) {
+                SubSelect subSelect = lateralSubSelect.getSubSelect();
+                if (subSelect.getSelectBody() != null) {
+                    processSelectBody(subSelect.getSelectBody());
+                }
+            }
+        }
+    }
+
+    /**
+     * 处理 sub join
+     *
+     * @param subJoin subJoin
+     * @return Table subJoin 中的主表
+     */
+    private List<Table> processSubJoin(SubJoin subJoin) {
+        List<Table> mainTables = new ArrayList<>();
+        if (subJoin.getJoinList() != null) {
+            List<Table> list = processFromItem(subJoin.getLeft());
+            mainTables.addAll(list);
+            mainTables = processJoins(mainTables, subJoin.getJoinList());
+        }
+        return mainTables;
+    }
+
+    /**
+     * 处理 joins
+     *
+     * @param mainTables 可以为 null
+     * @param joins      join 集合
+     * @return List<Table> 右连接查询的 Table 列表
+     */
+    private List<Table> processJoins(List<Table> mainTables, List<Join> joins) {
+        // join 表达式中最终的主表
+        Table mainTable = null;
+        // 当前 join 的左表
+        Table leftTable = null;
+
+        if (mainTables == null) {
+            mainTables = new ArrayList<>();
+        } else if (mainTables.size() == 1) {
+            mainTable = mainTables.get(0);
+            leftTable = mainTable;
+        }
+
+        //对于 on 表达式写在最后的 join,需要记录下前面多个 on 的表名
+        Deque<List<Table>> onTableDeque = new LinkedList<>();
+        for (Join join : joins) {
+            // 处理 on 表达式
+            FromItem joinItem = join.getRightItem();
+
+            // 获取当前 join 的表,subJoint 可以看作是一张表
+            List<Table> joinTables = null;
+            if (joinItem instanceof Table) {
+                joinTables = new ArrayList<>();
+                joinTables.add((Table) joinItem);
+            } else if (joinItem instanceof SubJoin) {
+                joinTables = processSubJoin((SubJoin) joinItem);
+            }
+
+            if (joinTables != null) {
+
+                // 如果是隐式内连接
+                if (join.isSimple()) {
+                    mainTables.addAll(joinTables);
+                    continue;
+                }
+
+                // 当前表是否忽略
+                Table joinTable = joinTables.get(0);
+
+                List<Table> onTables = null;
+                // 如果不要忽略,且是右连接,则记录下当前表
+                if (join.isRight()) {
+                    mainTable = joinTable;
+                    if (leftTable != null) {
+                        onTables = Collections.singletonList(leftTable);
+                    }
+                } else if (join.isLeft()) {
+                    onTables = Collections.singletonList(joinTable);
+                } else if (join.isInner()) {
+                    if (mainTable == null) {
+                        onTables = Collections.singletonList(joinTable);
+                    } else {
+                        onTables = Arrays.asList(mainTable, joinTable);
+                    }
+                    mainTable = null;
+                }
+
+                mainTables = new ArrayList<>();
+                if (mainTable != null) {
+                    mainTables.add(mainTable);
+                }
+
+                // 获取 join 尾缀的 on 表达式列表
+                Collection<Expression> originOnExpressions = join.getOnExpressions();
+                // 正常 join on 表达式只有一个,立刻处理
+                if (originOnExpressions.size() == 1 && onTables != null) {
+                    List<Expression> onExpressions = new LinkedList<>();
+                    onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables));
+                    join.setOnExpressions(onExpressions);
+                    leftTable = joinTable;
+                    continue;
+                }
+                // 表名压栈,忽略的表压入 null,以便后续不处理
+                onTableDeque.push(onTables);
+                // 尾缀多个 on 表达式的时候统一处理
+                if (originOnExpressions.size() > 1) {
+                    Collection<Expression> onExpressions = new LinkedList<>();
+                    for (Expression originOnExpression : originOnExpressions) {
+                        List<Table> currentTableList = onTableDeque.poll();
+                        if (CollectionUtils.isEmpty(currentTableList)) {
+                            onExpressions.add(originOnExpression);
+                        } else {
+                            onExpressions.add(builderExpression(originOnExpression, currentTableList));
+                        }
+                    }
+                    join.setOnExpressions(onExpressions);
+                }
+                leftTable = joinTable;
+            } else {
+                processOtherFromItem(joinItem);
+                leftTable = null;
+            }
+        }
+
+        return mainTables;
+    }
+
+    // ========== 和 TenantLineInnerInterceptor 存在差异的逻辑:关键,实现权限条件的拼接 ==========
+
+    /**
+     * 处理条件
+     *
+     * @param currentExpression 当前 where 条件
+     * @param table             单个表
+     */
+    protected Expression builderExpression(Expression currentExpression, Table table) {
+        return this.builderExpression(currentExpression, Collections.singletonList(table));
+    }
+
+    /**
+     * 处理条件
+     *
+     * @param currentExpression 当前 where 条件
+     * @param tables 多个表
+     */
+    protected Expression builderExpression(Expression currentExpression, List<Table> tables) {
+        // 没有表需要处理直接返回
+        if (CollectionUtils.isEmpty(tables)) {
+            return currentExpression;
+        }
+
+        // 第一步,获得 Table 对应的数据权限条件
+        Expression dataPermissionExpression = null;
+        for (Table table : tables) {
+            // 构建每个表的权限 Expression 条件
+            Expression expression = buildDataPermissionExpression(table);
+            if (expression == null) {
+                continue;
+            }
+            // 合并到 dataPermissionExpression 中
+            dataPermissionExpression = dataPermissionExpression == null ? expression
+                    : new AndExpression(dataPermissionExpression, expression);
+        }
+
+        // 第二步,合并多个 Expression 条件
+        if (dataPermissionExpression == null) {
+            return currentExpression;
+        }
+        if (currentExpression == null) {
+            return dataPermissionExpression;
+        }
+        // ① 如果表达式为 Or,则需要 (currentExpression) AND dataPermissionExpression
+        if (currentExpression instanceof OrExpression) {
+            return new AndExpression(new Parenthesis(currentExpression), dataPermissionExpression);
+        }
+        // ② 如果表达式为 And,则直接返回 where AND dataPermissionExpression
+        return new AndExpression(currentExpression, dataPermissionExpression);
+    }
+
+    /**
+     * 构建指定表的数据权限的 Expression 过滤条件
+     *
+     * @param table 表
+     * @return Expression 过滤条件
+     */
+    private Expression buildDataPermissionExpression(Table table) {
+        // 生成条件
+        Expression allExpression = null;
+        for (DataPermissionRule rule : ContextHolder.getRules()) {
+            // 判断表名是否匹配
+            if (!rule.getTableNames().contains(table.getName())) {
+                continue;
+            }
+            // 如果有匹配的规则,说明可重写。
+            // 为什么不是有 allExpression 非空才重写呢?在生成 column = value 过滤条件时,会因为 value 不存在,导致未重写。
+            // 这样导致第一次无 value,被标记成无需重写;但是第二次有 value,此时会需要重写。
+            ContextHolder.setRewrite(true);
+
+            // 单条规则的条件
+            String tableName = MyBatisPlusUtils.getTableName(table);
+            Expression oneExpress = rule.getExpression(tableName, table.getAlias());
+            if (oneExpress == null){
+                continue;
+            }
+            // 拼接到 allExpression 中
+            allExpression = allExpression == null ? oneExpress
+                    : new AndExpression(allExpression, oneExpress);
+        }
+
+        return allExpression;
+    }
+
+    /**
+     * 判断 SQL 是否重写。如果没有重写,则添加到 {@link MappedStatementCache} 中
+     *
+     * @param ms MappedStatement
+     */
+    private void addMappedStatementCache(MappedStatement ms) {
+        if (ContextHolder.getRewrite()) {
+            return;
+        }
+        // 无重写,进行添加
+        mappedStatementCache.addNoRewritable(ms, ContextHolder.getRules());
+    }
+
+    /**
+     * SQL 解析上下文,方便透传 {@link DataPermissionRule} 规则
+     *
+     * @author 张崧
+     */
+    static final class ContextHolder {
+
+        /**
+         * 该 {@link MappedStatement} 对应的规则
+         */
+        private static final ThreadLocal<List<DataPermissionRule>> RULES = ThreadLocal.withInitial(Collections::emptyList);
+        /**
+         * SQL 是否进行重写
+         */
+        private static final ThreadLocal<Boolean> REWRITE = ThreadLocal.withInitial(() -> Boolean.FALSE);
+
+        public static void init(List<DataPermissionRule> rules) {
+            RULES.set(rules);
+            REWRITE.set(false);
+        }
+
+        public static void clear() {
+            RULES.remove();
+            REWRITE.remove();
+        }
+
+        public static boolean getRewrite() {
+            return REWRITE.get();
+        }
+
+        public static void setRewrite(boolean rewrite) {
+            REWRITE.set(rewrite);
+        }
+
+        public static List<DataPermissionRule> getRules() {
+            return RULES.get();
+        }
+
+    }
+
+    /**
+     * {@link MappedStatement} 缓存
+     * 目前主要用于,记录 {@link DataPermissionRule} 是否对指定 {@link MappedStatement} 无效
+     * 如果无效,则可以避免 SQL 的解析,加快速度
+     *
+     * @author 张崧
+     */
+    static final class MappedStatementCache {
+
+        /**
+         * 指定数据权限规则,对指定 MappedStatement 无需重写(不生效)的缓存
+         *
+         * value:{@link MappedStatement#getId()} 编号
+         */
+        @Getter
+        private final Map<Class<? extends DataPermissionRule>, Set<String>> noRewritableMappedStatements = new ConcurrentHashMap<>();
+
+        /**
+         * 判断是否无需重写
+         * ps:虽然有点中文式英语,但是容易读懂即可
+         *
+         * @param ms MappedStatement
+         * @param rules 数据权限规则数组
+         * @return 是否无需重写
+         */
+        public boolean noRewritable(MappedStatement ms, List<DataPermissionRule> rules) {
+            // 如果规则为空,说明无需重写
+            if (CollUtil.isEmpty(rules)) {
+                return true;
+            }
+            // 任一规则不在 noRewritableMap 中,则说明可能需要重写
+            for (DataPermissionRule rule : rules) {
+                Set<String> mappedStatementIds = noRewritableMappedStatements.get(rule.getClass());
+                if (!CollUtil.contains(mappedStatementIds, ms.getId())) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /**
+         * 添加无需重写的 MappedStatement
+         *
+         * @param ms MappedStatement
+         * @param rules 数据权限规则数组
+         */
+        public void addNoRewritable(MappedStatement ms, List<DataPermissionRule> rules) {
+            for (DataPermissionRule rule : rules) {
+                Set<String> mappedStatementIds = noRewritableMappedStatements.get(rule.getClass());
+                if (CollUtil.isNotEmpty(mappedStatementIds)) {
+                    mappedStatementIds.add(ms.getId());
+                } else {
+                    noRewritableMappedStatements.put(rule.getClass(), CollUtil.newHashSet(ms.getId()));
+                }
+            }
+        }
+
+        /**
+         * 清空缓存
+         * 目前主要提供给单元测试
+         */
+        public void clear() {
+            noRewritableMappedStatements.clear();
+        }
+
+    }
+
+}

+ 36 - 0
framework/src/main/java/com/hys/app/framework/datapermission/rule/DataPermissionRule.java

@@ -0,0 +1,36 @@
+package com.hys.app.framework.datapermission.rule;
+
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import net.sf.jsqlparser.expression.Alias;
+import net.sf.jsqlparser.expression.Expression;
+
+import java.util.Set;
+
+/**
+ * 数据权限规则接口
+ * 通过实现接口,自定义数据规则。例如说,
+ *
+ * @author 张崧
+ */
+public interface DataPermissionRule {
+
+    /**
+     * 返回需要生效的表名数组
+     * 为什么需要该方法?Data Permission 数组基于 SQL 重写,通过 Where 返回只有权限的数据
+     *
+     * 如果需要基于实体名获得表名,可调用 {@link TableInfoHelper#getTableInfo(Class)} 获得
+     *
+     * @return 表名数组
+     */
+    Set<String> getTableNames();
+
+    /**
+     * 根据表名和别名,生成对应的 WHERE / OR 过滤条件
+     *
+     * @param tableName 表名
+     * @param tableAlias 别名,可能为空
+     * @return 过滤条件 Expression 表达式
+     */
+    Expression getExpression(String tableName, Alias tableAlias);
+
+}

+ 28 - 0
framework/src/main/java/com/hys/app/framework/datapermission/rule/DataPermissionRuleFactory.java

@@ -0,0 +1,28 @@
+package com.hys.app.framework.datapermission.rule;
+
+import java.util.List;
+
+/**
+ * {@link DataPermissionRule} 工厂接口
+ * 作为 {@link DataPermissionRule} 的容器,提供管理能力
+ *
+ * @author 张崧
+ */
+public interface DataPermissionRuleFactory {
+
+    /**
+     * 获得所有数据权限规则数组
+     *
+     * @return 数据权限规则数组
+     */
+    List<DataPermissionRule> getDataPermissionRules();
+
+    /**
+     * 获得指定 Mapper 的数据权限规则数组
+     *
+     * @param mappedStatementId 指定 Mapper 的编号
+     * @return 数据权限规则数组
+     */
+    List<DataPermissionRule> getDataPermissionRule(String mappedStatementId);
+
+}

+ 62 - 0
framework/src/main/java/com/hys/app/framework/datapermission/rule/DataPermissionRuleFactoryImpl.java

@@ -0,0 +1,62 @@
+package com.hys.app.framework.datapermission.rule;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
+import com.hys.app.framework.datapermission.annotation.DataPermission;
+import com.hys.app.framework.datapermission.aop.DataPermissionContextHolder;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 默认的 DataPermissionRuleFactoryImpl 实现类
+ * 支持通过 {@link DataPermissionContextHolder} 过滤数据权限
+ *
+ * @author 张崧
+ */
+@RequiredArgsConstructor
+public class DataPermissionRuleFactoryImpl implements DataPermissionRuleFactory {
+
+    /**
+     * 数据权限规则数组
+     */
+    private final List<DataPermissionRule> rules;
+
+    @Override
+    public List<DataPermissionRule> getDataPermissionRules() {
+        return rules;
+    }
+
+    @Override // mappedStatementId 参数,暂时没有用。以后,可以基于 mappedStatementId + DataPermission 进行缓存
+    public List<DataPermissionRule> getDataPermissionRule(String mappedStatementId) {
+        // 1. 无数据权限
+        if (CollUtil.isEmpty(rules)) {
+            return Collections.emptyList();
+        }
+        // 2. 未配置,则默认开启
+        DataPermission dataPermission = DataPermissionContextHolder.get();
+        if (dataPermission == null) {
+            return rules;
+        }
+        // 3. 已配置,但禁用
+        if (!dataPermission.enable()) {
+            return Collections.emptyList();
+        }
+
+        // 4. 已配置,只选择部分规则
+        if (ArrayUtil.isNotEmpty(dataPermission.includeRules())) {
+            return rules.stream().filter(rule -> ArrayUtil.contains(dataPermission.includeRules(), rule.getClass()))
+                    .collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询
+        }
+        // 5. 已配置,只排除部分规则
+        if (ArrayUtil.isNotEmpty(dataPermission.excludeRules())) {
+            return rules.stream().filter(rule -> !ArrayUtil.contains(dataPermission.excludeRules(), rule.getClass()))
+                    .collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询
+        }
+        // 6. 已配置,全部规则
+        return rules;
+    }
+
+}

+ 43 - 0
framework/src/main/java/com/hys/app/framework/datapermission/util/DataPermissionUtils.java

@@ -0,0 +1,43 @@
+package com.hys.app.framework.datapermission.util;
+
+import com.hys.app.framework.datapermission.annotation.DataPermission;
+import com.hys.app.framework.datapermission.aop.DataPermissionContextHolder;
+import lombok.SneakyThrows;
+
+/**
+ * 数据权限 Util
+ *
+ * @author 张崧
+ */
+public class DataPermissionUtils {
+
+    private static DataPermission DATA_PERMISSION_DISABLE;
+
+    @DataPermission(enable = false)
+    @SneakyThrows
+    private static DataPermission getDisableDataPermissionDisable() {
+        if (DATA_PERMISSION_DISABLE == null) {
+            DATA_PERMISSION_DISABLE = DataPermissionUtils.class
+                    .getDeclaredMethod("getDisableDataPermissionDisable")
+                    .getAnnotation(DataPermission.class);
+        }
+        return DATA_PERMISSION_DISABLE;
+    }
+
+    /**
+     * 忽略数据权限,执行对应的逻辑
+     *
+     * @param runnable 逻辑
+     */
+    public static void executeIgnore(Runnable runnable) {
+        DataPermission dataPermission = getDisableDataPermissionDisable();
+        DataPermissionContextHolder.add(dataPermission);
+        try {
+            // 执行 runnable
+            runnable.run();
+        } finally {
+            DataPermissionContextHolder.remove();
+        }
+    }
+
+}

+ 51 - 0
framework/src/main/java/com/hys/app/framework/elasticsearch/EsConfig.java

@@ -0,0 +1,51 @@
+package com.hys.app.framework.elasticsearch;
+
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Created by kingapex on 2018/7/18.
+ *
+ * @author kingapex
+ * @version 1.0
+ * @since 7.0.0
+ * 2018/7/18
+ */
+@Configuration
+public class EsConfig {
+
+//    @Value("${spring.data.elasticsearch.cluster-name}")
+    private String clusterName;
+
+//    @Value("${spring.data.elasticsearch.xpack.security.user:#{null}}")
+    private String userPass;
+
+//    @Value("${spring.data.elasticsearch.cluster-nodes}")
+    private String nodes;
+
+    /**
+     * 索引名称
+     */
+//    @Value("${spring.data.elasticsearch.index-name}")
+    private String indexName;
+
+    public String getIndexName() {
+        return indexName;
+    }
+
+    public void setIndexName(String indexName) {
+        this.indexName = indexName;
+    }
+
+//    @Bean
+//    public ElasticsearchTemplate elasticsearchTemplate() {
+//        EsTemplateBuilder esTemplateBuilder = null;
+//        if(!StringUtil.isEmpty(userPass)){
+//            esTemplateBuilder = new DefaultEsTemplateBuilder().setClusterName(clusterName).setClusterNodes(nodes).setUserPass(userPass);
+//        }else{
+//            esTemplateBuilder = new DefaultEsTemplateBuilder().setClusterName(clusterName).setClusterNodes(nodes);
+//        }
+//        return esTemplateBuilder.build();
+//    }
+
+
+}

+ 56 - 0
framework/src/main/java/com/hys/app/framework/elasticsearch/EsIndexInit.java

@@ -0,0 +1,56 @@
+package com.hys.app.framework.elasticsearch;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * 初始es索引
+ * @author Administrator
+ */
+@Component
+public class EsIndexInit {
+
+//    @Autowired
+    protected ElasticsearchTemplate elasticsearchTemplate;
+
+    @Autowired
+    protected EsConfig esConfig;
+
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+
+    public void initEs(){
+
+        //拼装索引名称
+        String goodsIndices = esConfig.getIndexName() + "_" + EsSettings.GOODS_INDEX_NAME;
+        String ptIndices = esConfig.getIndexName() + "_" + EsSettings.PINTUAN_INDEX_NAME;
+
+        //检测商品索引是否存在
+        if (!elasticsearchTemplate.indexExists(goodsIndices)) {
+            logger.debug("系统检测到索引{}不存在,创建索引和Mapping",goodsIndices);
+            elasticsearchTemplate.createIndex(goodsIndices);
+            Map goodsMapping = EsMappingUtil.createGoodsMapping();
+            elasticsearchTemplate.putMapping(goodsIndices, EsSettings.GOODS_TYPE_NAME, goodsMapping);
+        }else {
+            logger.debug("系统检测到索引{}已经存在",goodsIndices);
+        }
+
+        //检测拼团索引是否存在
+        if (!elasticsearchTemplate.indexExists(ptIndices)) {
+            logger.debug("系统检测到索引{}不存在,创建索引和Mapping",ptIndices);
+            elasticsearchTemplate.createIndex(ptIndices);
+            Map pingTuanMapping = EsMappingUtil.createPingTuanMapping();
+            elasticsearchTemplate.putMapping(ptIndices, EsSettings.PINTUAN_TYPE_NAME, pingTuanMapping);
+        }else {
+            logger.debug("系统检测到索引{}已经存在",ptIndices);
+        }
+
+    }
+
+}

+ 84 - 0
framework/src/main/java/com/hys/app/framework/elasticsearch/EsMappingUtil.java

@@ -0,0 +1,84 @@
+package com.hys.app.framework.elasticsearch;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 组装esde mapping
+ * @author fk
+ * @version 1.0
+ * @since 7.2.2
+ * 2021年04月14日18:06:54
+ */
+public class EsMappingUtil {
+
+
+    /**
+     * 创建商品mapping
+     *
+     * @return
+     */
+    public static Map createGoodsMapping() {
+
+        Map goodsMap = new HashMap();
+
+        goodsMap.put("goodsId", new MyMap().put("type", "long").getMap());
+        goodsMap.put("goodsName", new MyMap().put("type", "text")
+                .put("fields", new MyMap().put("keyword", new MyMap().put("type", "text").getMap()).getMap())
+                .put("analyzer", "ik_max_word").getMap());
+        goodsMap.put("thumbnail", new MyMap().put("type", "text").getMap());
+        goodsMap.put("small", new MyMap().put("type", "text").getMap());
+        goodsMap.put("buyCount", new MyMap().put("type", "integer").getMap());
+        goodsMap.put("sellerId", new MyMap().put("type", "long").getMap());
+        goodsMap.put("sellerName", new MyMap().put("type", "text").getMap());
+        goodsMap.put("shopCatId", new MyMap().put("type", "long").getMap());
+        goodsMap.put("shopCatPath", new MyMap().put("type", "text").getMap());
+        goodsMap.put("commentNum", new MyMap().put("type", "integer").getMap());
+        goodsMap.put("grade", new MyMap().put("type", "double").getMap());
+        goodsMap.put("price", new MyMap().put("type", "double").getMap());
+        goodsMap.put("brand", new MyMap().put("type", "long").getMap());
+        goodsMap.put("categoryId", new MyMap().put("type", "long").getMap());
+        goodsMap.put("categoryPath", new MyMap().put("type", "text").getMap());
+        goodsMap.put("disabled", new MyMap().put("type", "integer").getMap());
+        goodsMap.put("marketEnable", new MyMap().put("type", "integer").getMap());
+        goodsMap.put("isAuth", new MyMap().put("type", "integer").getMap());
+        goodsMap.put("intro", new MyMap().put("type", "text").getMap());
+        goodsMap.put("selfOperated", new MyMap().put("type", "integer").getMap());
+        goodsMap.put("goodsTags", new MyMap().put("type", "text").getMap());
+        goodsMap.put("saleChannel", new MyMap().put("type", "text").getMap());
+        goodsMap.put("goodsType", new MyMap().put("type", "text").getMap());
+        goodsMap.put("priority", new MyMap().put("type", "integer").getMap());
+
+        Map paramPro = new MyMap().put("name", new MyMap().put("type", "keyword").getMap()).put("value", new MyMap().put("type", "keyword").getMap()).getMap();
+        goodsMap.put("params", new MyMap().put("type", "nested").put("properties", paramPro).getMap());
+
+        goodsMap.put("goodsTags", new MyMap().put("type", "text").getMap());
+        goodsMap.put("saleChannel", new MyMap().put("type", "text").getMap());
+        goodsMap.put("haveSpec", new MyMap().put("type", "integer").getMap());
+        goodsMap.put("sn", new MyMap().put("type", "text").getMap());
+
+        return new MyMap().put("properties", goodsMap).getMap();
+    }
+
+    /**
+     * 创建拼团mapping
+     *
+     * @return
+     */
+    public static Map createPingTuanMapping() {
+
+        Map pingTuanMap = new HashMap();
+        pingTuanMap.put("buyCount", new MyMap().put("type", "integer").getMap());
+        pingTuanMap.put("categoryId", new MyMap().put("type", "long").getMap());
+        pingTuanMap.put("categoryPath", new MyMap().put("type", "text").getMap());
+        pingTuanMap.put("goodsId", new MyMap().put("type", "long").getMap());
+        pingTuanMap.put("goodsName", new MyMap().put("type", "text").put("analyzer", "ik_max_word").getMap());
+        pingTuanMap.put("originPrice", new MyMap().put("type", "double").getMap());
+        pingTuanMap.put("salesPrice", new MyMap().put("type", "double").getMap());
+        pingTuanMap.put("skuId", new MyMap().put("type", "long").getMap());
+        pingTuanMap.put("thumbnail", new MyMap().put("type", "text").getMap());
+        return new MyMap().put("properties", pingTuanMap).getMap();
+    }
+
+}

+ 58 - 0
framework/src/main/java/com/hys/app/framework/elasticsearch/EsSettings.java

@@ -0,0 +1,58 @@
+package com.hys.app.framework.elasticsearch;
+
+/**
+ *
+ * es索引设置
+ * @author kingapex
+ * @version 1.0
+ * @since 7.1.0
+ * 2019-03-21
+ */
+
+public class EsSettings {
+
+    /**
+     * 商品索引后缀
+     */
+    public static final String GOODS_INDEX_NAME="goods";
+
+    /**
+     * 商品type名字
+     */
+    public static final String GOODS_TYPE_NAME="goods";
+
+    /**
+     * 拼团索引后缀
+     */
+    public static final String PINTUAN_INDEX_NAME="pt";
+
+
+    /**
+     * 拼团类型名字
+     */
+    public static final String PINTUAN_TYPE_NAME="pintuan_goods";
+
+
+    /**
+     * 日志索引前缀
+     */
+    public static  final String LOG_INDEX_NAME = "log-index-";
+
+    /**
+     * 日志type
+     */
+    public static  final String LOG_TYPE_NAME = "log";
+
+    /**
+     * 门店索引后缀
+     */
+    public static final String BRANCH_INDEX_NAME = "branch";
+
+    /**
+     * 门店type名字
+     */
+    public static final String BRANCH_TYPE_NAME = "branch";
+
+
+
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است