瀏覽代碼

first commit

guoenzhou 2 年之前
當前提交
2502c5d0f0
共有 100 個文件被更改,包括 8668 次插入0 次删除
  1. 二進制
      .DS_Store
  2. 21 0
      .gitignore
  3. 191 0
      LICENSE
  4. 115 0
      README.md
  5. 37 0
      eladmin-common/pom.xml
  6. 30 0
      eladmin-common/src/main/java/me/zhengjie/annotation/AnonymousAccess.java
  7. 47 0
      eladmin-common/src/main/java/me/zhengjie/annotation/DataPermission.java
  8. 49 0
      eladmin-common/src/main/java/me/zhengjie/annotation/Limit.java
  9. 90 0
      eladmin-common/src/main/java/me/zhengjie/annotation/Query.java
  10. 91 0
      eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousDeleteMapping.java
  11. 90 0
      eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousGetMapping.java
  12. 91 0
      eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPatchMapping.java
  13. 91 0
      eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPostMapping.java
  14. 91 0
      eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPutMapping.java
  15. 99 0
      eladmin-common/src/main/java/me/zhengjie/aspect/LimitAspect.java
  16. 27 0
      eladmin-common/src/main/java/me/zhengjie/aspect/LimitType.java
  17. 40 0
      eladmin-common/src/main/java/me/zhengjie/base/BaseDTO.java
  18. 85 0
      eladmin-common/src/main/java/me/zhengjie/base/BaseEntity.java
  19. 53 0
      eladmin-common/src/main/java/me/zhengjie/base/BaseMapper.java
  20. 64 0
      eladmin-common/src/main/java/me/zhengjie/base/BaseResponse.java
  21. 33 0
      eladmin-common/src/main/java/me/zhengjie/base/QueryPageParams.java
  22. 45 0
      eladmin-common/src/main/java/me/zhengjie/config/AuditorConfig.java
  23. 37 0
      eladmin-common/src/main/java/me/zhengjie/config/ElPermissionConfig.java
  24. 60 0
      eladmin-common/src/main/java/me/zhengjie/config/FileProperties.java
  25. 215 0
      eladmin-common/src/main/java/me/zhengjie/config/RedisConfig.java
  26. 38 0
      eladmin-common/src/main/java/me/zhengjie/config/RsaProperties.java
  27. 148 0
      eladmin-common/src/main/java/me/zhengjie/config/SwaggerConfig.java
  28. 98 0
      eladmin-common/src/main/java/me/zhengjie/exception/BadConfigurationException.java
  29. 40 0
      eladmin-common/src/main/java/me/zhengjie/exception/BadRequestException.java
  30. 34 0
      eladmin-common/src/main/java/me/zhengjie/exception/EntityExistException.java
  31. 34 0
      eladmin-common/src/main/java/me/zhengjie/exception/EntityNotFoundException.java
  32. 52 0
      eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java
  33. 113 0
      eladmin-common/src/main/java/me/zhengjie/exception/handler/GlobalExceptionHandler.java
  34. 265 0
      eladmin-common/src/main/java/me/zhengjie/utils/BeanMapUtils.java
  35. 58 0
      eladmin-common/src/main/java/me/zhengjie/utils/CacheKey.java
  36. 43 0
      eladmin-common/src/main/java/me/zhengjie/utils/CallBack.java
  37. 47 0
      eladmin-common/src/main/java/me/zhengjie/utils/CloseUtil.java
  38. 40 0
      eladmin-common/src/main/java/me/zhengjie/utils/ConnectionUtil.java
  39. 206 0
      eladmin-common/src/main/java/me/zhengjie/utils/DateUtil.java
  40. 47 0
      eladmin-common/src/main/java/me/zhengjie/utils/ElAdminConstant.java
  41. 127 0
      eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java
  42. 356 0
      eladmin-common/src/main/java/me/zhengjie/utils/FileUtil.java
  43. 117 0
      eladmin-common/src/main/java/me/zhengjie/utils/ImageUtil.java
  44. 54 0
      eladmin-common/src/main/java/me/zhengjie/utils/MessageSendUtils.java
  45. 63 0
      eladmin-common/src/main/java/me/zhengjie/utils/PageUtil.java
  46. 208 0
      eladmin-common/src/main/java/me/zhengjie/utils/QueryHelp.java
  47. 721 0
      eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java
  48. 33 0
      eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java
  49. 198 0
      eladmin-common/src/main/java/me/zhengjie/utils/RsaUtils.java
  50. 161 0
      eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java
  51. 145 0
      eladmin-common/src/main/java/me/zhengjie/utils/SpringContextHolder.java
  52. 294 0
      eladmin-common/src/main/java/me/zhengjie/utils/StringUtils.java
  53. 37 0
      eladmin-common/src/main/java/me/zhengjie/utils/ThrowableUtil.java
  54. 66 0
      eladmin-common/src/main/java/me/zhengjie/utils/TranslatorUtil.java
  55. 45 0
      eladmin-common/src/main/java/me/zhengjie/utils/ValidationUtil.java
  56. 111 0
      eladmin-common/src/main/java/me/zhengjie/utils/WxUtil.java
  57. 50 0
      eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeBiEnum.java
  58. 46 0
      eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeEnum.java
  59. 53 0
      eladmin-common/src/main/java/me/zhengjie/utils/enums/DataScopeEnum.java
  60. 74 0
      eladmin-common/src/main/java/me/zhengjie/utils/enums/RequestMethodEnum.java
  61. 26 0
      eladmin-common/src/test/java/me/zhengjie/utils/DateUtilsTest.java
  62. 32 0
      eladmin-common/src/test/java/me/zhengjie/utils/EncryptUtilsTest.java
  63. 36 0
      eladmin-common/src/test/java/me/zhengjie/utils/FileUtilTest.java
  64. 43 0
      eladmin-common/src/test/java/me/zhengjie/utils/StringUtilsTest.java
  65. 39 0
      eladmin-generator/pom.xml
  66. 97 0
      eladmin-generator/src/main/java/me/zhengjie/domain/ColumnInfo.java
  67. 78 0
      eladmin-generator/src/main/java/me/zhengjie/domain/GenConfig.java
  68. 48 0
      eladmin-generator/src/main/java/me/zhengjie/domain/vo/TableInfo.java
  69. 34 0
      eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoRepository.java
  70. 33 0
      eladmin-generator/src/main/java/me/zhengjie/repository/GenConfigRepository.java
  71. 51 0
      eladmin-generator/src/main/java/me/zhengjie/rest/GenConfigController.java
  72. 107 0
      eladmin-generator/src/main/java/me/zhengjie/rest/GeneratorController.java
  73. 40 0
      eladmin-generator/src/main/java/me/zhengjie/service/GenConfigService.java
  74. 96 0
      eladmin-generator/src/main/java/me/zhengjie/service/GeneratorService.java
  75. 67 0
      eladmin-generator/src/main/java/me/zhengjie/service/impl/GenConfigServiceImpl.java
  76. 205 0
      eladmin-generator/src/main/java/me/zhengjie/service/impl/GeneratorServiceImpl.java
  77. 54 0
      eladmin-generator/src/main/java/me/zhengjie/utils/ColUtil.java
  78. 421 0
      eladmin-generator/src/main/java/me/zhengjie/utils/GenUtil.java
  79. 22 0
      eladmin-logging/pom.xml
  80. 31 0
      eladmin-logging/src/main/java/me/zhengjie/annotation/Log.java
  81. 98 0
      eladmin-logging/src/main/java/me/zhengjie/aspect/LogAspect.java
  82. 80 0
      eladmin-logging/src/main/java/me/zhengjie/domain/Log.java
  83. 39 0
      eladmin-logging/src/main/java/me/zhengjie/repository/LogRepository.java
  84. 109 0
      eladmin-logging/src/main/java/me/zhengjie/rest/LogController.java
  85. 92 0
      eladmin-logging/src/main/java/me/zhengjie/service/LogService.java
  86. 46 0
      eladmin-logging/src/main/java/me/zhengjie/service/dto/LogErrorDTO.java
  87. 39 0
      eladmin-logging/src/main/java/me/zhengjie/service/dto/LogQueryCriteria.java
  88. 40 0
      eladmin-logging/src/main/java/me/zhengjie/service/dto/LogSmallDTO.java
  89. 172 0
      eladmin-logging/src/main/java/me/zhengjie/service/impl/LogServiceImpl.java
  90. 31 0
      eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogErrorMapper.java
  91. 31 0
      eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogSmallMapper.java
  92. 二進制
      eladmin-system/.DS_Store
  93. 147 0
      eladmin-system/pom.xml
  94. 二進制
      eladmin-system/src/.DS_Store
  95. 二進制
      eladmin-system/src/main/.DS_Store
  96. 二進制
      eladmin-system/src/main/java/.DS_Store
  97. 二進制
      eladmin-system/src/main/java/me/.DS_Store
  98. 二進制
      eladmin-system/src/main/java/me/zhengjie/.DS_Store
  99. 82 0
      eladmin-system/src/main/java/me/zhengjie/AppRun.java
  100. 88 0
      eladmin-system/src/main/java/me/zhengjie/config/ConfigurerAdapter.java

二進制
.DS_Store


+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+<<<<<<< HEAD
+### IDEA ###
+.idea/*
+*.iml
+*/target/*
+*/*.iml
+/.gradle/
+=======
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+>>>>>>> 0829603bd7631a030f3f3e364b7a5873f42efb1f

+ 191 - 0
LICENSE

@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "{}" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+   Copyright 2019-2020 Zheng Jie
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 115 - 0
README.md

@@ -0,0 +1,115 @@
+<<<<<<< HEAD
+<h1 style="text-align: center">EL-ADMIN 后台管理系统</h1>
+<div style="text-align: center">
+
+[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/elunez/eladmin/blob/master/LICENSE)
+[![star](https://gitee.com/elunez/eladmin/badge/star.svg?theme=white)](https://gitee.com/elunez/eladmin)
+[![GitHub stars](https://img.shields.io/github/stars/elunez/eladmin.svg?style=social&label=Stars)](https://github.com/elunez/eladmin)
+[![GitHub forks](https://img.shields.io/github/forks/elunez/eladmin.svg?style=social&label=Fork)](https://github.com/elunez/eladmin)
+
+</div>
+
+#### 项目简介
+一个基于 Spring Boot 2.1.0 、 Spring Boot Jpa、 JWT、Spring Security、Redis、Vue的前后端分离的后台管理系统
+
+**开发文档:**  [https://el-admin.vip](https://el-admin.vip)
+
+**体验地址:**  [https://el-admin.xin](https://el-admin.xin)
+
+**账号密码:** `admin / 123456`
+
+#### 项目源码
+
+|     |   后端源码  |   前端源码  |
+|---  |--- | --- |
+|  github   |  https://github.com/elunez/eladmin   |  https://github.com/elunez/eladmin-web   |
+|  码云   |  https://gitee.com/elunez/eladmin   |  https://gitee.com/elunez/eladmin-web   |
+
+#### 主要特性
+- 使用最新技术栈,社区资源丰富。
+- 高效率开发,代码生成器可一键生成前后端代码
+- 支持数据字典,可方便地对一些状态进行管理
+- 支持接口限流,避免恶意请求导致服务层压力过大
+- 支持接口级别的功能权限与数据权限,可自定义操作
+- 自定义权限注解与匿名接口注解,可快速对接口拦截与放行
+- 对一些常用地前端组件封装:表格数据请求、数据字典等
+- 前后端统一异常拦截处理,统一输出异常,避免繁琐的判断
+- 支持在线用户管理与服务器性能监控,支持限制单用户登录
+- 支持运维管理,可方便地对远程服务器的应用进行部署与管理
+
+####  系统功能
+- 用户管理:提供用户的相关配置,新增用户后,默认密码为123456
+- 角色管理:对权限与菜单进行分配,可根据部门设置角色的数据权限
+- 菜单管理:已实现菜单动态路由,后端可配置化,支持多级菜单
+- 部门管理:可配置系统组织架构,树形表格展示
+- 岗位管理:配置各个部门的职位
+- 字典管理:可维护常用一些固定的数据,如:状态,性别等
+- 系统日志:记录用户操作日志与异常日志,方便开发人员定位排错
+- SQL监控:采用druid 监控数据库访问性能,默认用户名admin,密码123456
+- 定时任务:整合Quartz做定时任务,加入任务日志,任务运行情况一目了然
+- 代码生成:高灵活度生成前后端代码,减少大量重复的工作任务
+- 邮件工具:配合富文本,发送html格式的邮件
+- 七牛云存储:可同步七牛云存储的数据到系统,无需登录七牛云直接操作云数据
+- 支付宝支付:整合了支付宝支付并且提供了测试账号,可自行测试
+- 服务监控:监控服务器的负载情况
+- 运维管理:一键部署你的应用
+
+#### 项目结构
+项目采用按功能分模块的开发方式,结构如下
+
+- `eladmin-common` 为系统的公共模块,各种工具类,公共配置存在该模块
+
+- `eladmin-system` 为系统核心模块也是项目入口模块,也是最终需要打包部署的模块
+
+- `eladmin-logging` 为系统的日志模块,其他模块如果需要记录日志需要引入该模块
+
+- `eladmin-tools` 为第三方工具模块,包含:图床、邮件、云存储、本地存储、支付宝
+
+- `eladmin-generator` 为系统的代码生成模块,代码生成的模板在 system 模块中
+
+#### 详细结构
+
+```
+- eladmin-common 公共模块
+    - annotation 为系统自定义注解
+    - aspect 自定义注解的切面
+    - base 提供了Entity、DTO基类和mapstruct的通用mapper
+    - config 自定义权限实现、redis配置、swagger配置、Rsa配置等
+    - exception 项目统一异常的处理
+    - utils 系统通用工具类
+- eladmin-system 系统核心模块(系统启动入口)
+	- config 配置跨域与静态资源,与数据权限
+	    - thread 线程池相关
+	- modules 系统相关模块(登录授权、系统监控、定时任务、运维管理等)
+- eladmin-logging 系统日志模块
+- eladmin-tools 系统第三方工具模块
+- eladmin-generator 系统代码生成模块
+```
+
+#### 特别鸣谢
+
+- 感谢 [JetBrains](https://www.jetbrains.com/) 提供的非商业开源软件开发授权
+
+- 感谢 [七牛云](https://www.qiniu.com/) 提供的免费云存储与CDN加速支持
+
+- 感谢 [PanJiaChen](https://github.com/PanJiaChen/vue-element-admin) 大佬提供的前端模板
+
+- 感谢 [Moxun](https://github.com/moxun1639) 大佬提供的前端 Curd 通用组件
+
+- 感谢 [zhy6599](https://gitee.com/zhy6599) 大佬提供的后端运维管理相关功能
+
+- 感谢 [j.yao.SUSE](https://github.com/everhopingandwaiting) 大佬提供的匿名接口与Redis限流等功能
+
+- 感谢 [d15801543974](https://github.com/d15801543974) 大佬提供的基于注解的通用查询方式
+
+#### 项目捐赠
+项目的发展离不开你的支持,请作者喝杯咖啡吧☕  [Donate](https://el-admin.vip/donation/)
+
+#### 反馈交流
+- QQ交流群:一群:<strike>891137268</strike> 已满、二群:947578238
+=======
+## iot-ykt
+
+永天-一卡通
+
+>>>>>>> 0829603bd7631a030f3f3e364b7a5873f42efb1f

+ 37 - 0
eladmin-common/pom.xml

@@ -0,0 +1,37 @@
+<?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>eladmin</artifactId>
+        <groupId>me.zhengjie</groupId>
+        <version>2.6</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <properties>
+        <hutool.version>5.3.4</hutool.version>
+    </properties>
+
+    <artifactId>eladmin-common</artifactId>
+    <name>公共模块</name>
+
+    <dependencies>
+        <!--工具包-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
+        <!-- rabbitmq -->
+        <dependency>
+            <groupId>com.rabbitmq</groupId>
+            <artifactId>amqp-client</artifactId>
+            <version>3.4.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.13</version>
+        </dependency>
+    </dependencies>
+</project>

+ 30 - 0
eladmin-common/src/main/java/me/zhengjie/annotation/AnonymousAccess.java

@@ -0,0 +1,30 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * @author jacky
+ *  用于标记匿名访问方法
+ */
+@Inherited
+@Documented
+@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AnonymousAccess {
+
+}

+ 47 - 0
eladmin-common/src/main/java/me/zhengjie/annotation/DataPermission.java

@@ -0,0 +1,47 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ *   用于判断是否过滤数据权限
+ *   1、如果没有用到 @OneToOne 这种关联关系,只需要填写 fieldName [参考:DeptQueryCriteria.class]
+ *   2、如果用到了 @OneToOne ,fieldName 和 joinName 都需要填写,拿UserQueryCriteria.class举例:
+ *   应该是 @DataPermission(joinName = "dept", fieldName = "id")
+ * </p>
+ * @author Zheng Jie
+ * @website https://el-admin.vip
+ * @date 2020-05-07
+ **/
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DataPermission {
+
+    /**
+     * Entity 中的字段名称
+     */
+    String fieldName() default "";
+
+    /**
+     * Entity 中与部门关联的字段名称
+     */
+    String joinName() default "";
+}

+ 49 - 0
eladmin-common/src/main/java/me/zhengjie/annotation/Limit.java

@@ -0,0 +1,49 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.annotation;
+
+import me.zhengjie.aspect.LimitType;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author jacky
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Limit {
+
+    // 资源名称,用于描述接口功能
+    String name() default "";
+
+    // 资源 key
+    String key() default "";
+
+    // key prefix
+    String prefix() default "";
+
+    // 时间的,单位秒
+    int period();
+
+    // 限制访问次数
+    int count();
+
+    // 限制类型
+    LimitType limitType() default LimitType.CUSTOMER;
+
+}

+ 90 - 0
eladmin-common/src/main/java/me/zhengjie/annotation/Query.java

@@ -0,0 +1,90 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-6-4 13:52:30
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Query {
+
+    // Dong ZhaoYang 2017/8/7 基本对象的属性名
+    String propName() default "";
+    // Dong ZhaoYang 2017/8/7 查询方式
+    Type type() default Type.EQUAL;
+
+    /**
+     * 连接查询的属性名,如User类中的dept
+     */
+    String joinName() default "";
+
+    /**
+     * 默认左连接
+     */
+    Join join() default Join.LEFT;
+
+    /**
+     * 多字段模糊搜索,仅支持String类型字段,多个用逗号隔开, 如@Query(blurry = "email,username")
+     */
+    String blurry() default "";
+
+    enum Type {
+        // jie 2019/6/4 相等
+        EQUAL
+        // Dong ZhaoYang 2017/8/7 大于等于
+        , GREATER_THAN
+        // Dong ZhaoYang 2017/8/7 小于等于
+        , LESS_THAN
+        // Dong ZhaoYang 2017/8/7 中模糊查询
+        , INNER_LIKE
+        // Dong ZhaoYang 2017/8/7 左模糊查询
+        , LEFT_LIKE
+        // Dong ZhaoYang 2017/8/7 右模糊查询
+        , RIGHT_LIKE
+        // Dong ZhaoYang 2017/8/7 小于
+        , LESS_THAN_NQ
+        // jie 2019/6/4 包含
+        , IN
+        // 不包含
+        , NOT_IN
+        // 不等于
+        ,NOT_EQUAL
+        // between
+        ,BETWEEN
+        // 不为空
+        ,NOT_NULL
+        // 为空
+        ,IS_NULL
+    }
+
+    /**
+     * @author Zheng Jie
+     * 适用于简单连接查询,复杂的请自定义该注解,或者使用sql查询
+     */
+    enum Join {
+        /** jie 2019-6-4 13:18:30 */
+        LEFT, RIGHT, INNER
+    }
+
+}
+

+ 91 - 0
eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousDeleteMapping.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.zhengjie.annotation.rest;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import me.zhengjie.annotation.AnonymousAccess;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+/**
+ * Annotation for mapping HTTP {@code DELETE} requests onto specific handler
+ * methods.
+ * 支持匿名访问  DeleteMapping
+ *
+ * @author liaojinlong
+ * @see AnonymousGetMapping
+ * @see AnonymousPostMapping
+ * @see AnonymousPutMapping
+ * @see AnonymousPatchMapping
+ * @see RequestMapping
+ */
+@AnonymousAccess
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RequestMapping(method = RequestMethod.DELETE)
+public @interface AnonymousDeleteMapping {
+
+    /**
+     * Alias for {@link RequestMapping#name}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String name() default "";
+
+    /**
+     * Alias for {@link RequestMapping#value}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] value() default {};
+
+    /**
+     * Alias for {@link RequestMapping#path}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] path() default {};
+
+    /**
+     * Alias for {@link RequestMapping#params}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] params() default {};
+
+    /**
+     * Alias for {@link RequestMapping#headers}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] headers() default {};
+
+    /**
+     * Alias for {@link RequestMapping#consumes}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] consumes() default {};
+
+    /**
+     * Alias for {@link RequestMapping#produces}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] produces() default {};
+
+}

+ 90 - 0
eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousGetMapping.java

@@ -0,0 +1,90 @@
+/*
+ * Copyright 2002-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.zhengjie.annotation.rest;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import me.zhengjie.annotation.AnonymousAccess;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+/**
+ * Annotation for mapping HTTP {@code GET} requests onto specific handler
+ * methods.
+ * <p>
+ * 支持匿名访问   GetMapping
+ *
+ * @author liaojinlong
+ * @see RequestMapping
+ */
+@AnonymousAccess
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RequestMapping(method = RequestMethod.GET)
+public @interface AnonymousGetMapping {
+
+    /**
+     * Alias for {@link RequestMapping#name}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String name() default "";
+
+    /**
+     * Alias for {@link RequestMapping#value}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] value() default {};
+
+    /**
+     * Alias for {@link RequestMapping#path}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] path() default {};
+
+    /**
+     * Alias for {@link RequestMapping#params}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] params() default {};
+
+    /**
+     * Alias for {@link RequestMapping#headers}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] headers() default {};
+
+    /**
+     * Alias for {@link RequestMapping#consumes}.
+     *
+     * @since 4.3.5
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] consumes() default {};
+
+    /**
+     * Alias for {@link RequestMapping#produces}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] produces() default {};
+
+}

+ 91 - 0
eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPatchMapping.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.zhengjie.annotation.rest;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import me.zhengjie.annotation.AnonymousAccess;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+/**
+ * Annotation for mapping HTTP {@code PATCH} requests onto specific handler
+ * methods.
+ * * 支持匿名访问    PatchMapping
+ *
+ * @author liaojinlong
+ * @see AnonymousGetMapping
+ * @see AnonymousPostMapping
+ * @see AnonymousPutMapping
+ * @see AnonymousDeleteMapping
+ * @see RequestMapping
+ */
+@AnonymousAccess
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RequestMapping(method = RequestMethod.PATCH)
+public @interface AnonymousPatchMapping {
+
+    /**
+     * Alias for {@link RequestMapping#name}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String name() default "";
+
+    /**
+     * Alias for {@link RequestMapping#value}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] value() default {};
+
+    /**
+     * Alias for {@link RequestMapping#path}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] path() default {};
+
+    /**
+     * Alias for {@link RequestMapping#params}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] params() default {};
+
+    /**
+     * Alias for {@link RequestMapping#headers}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] headers() default {};
+
+    /**
+     * Alias for {@link RequestMapping#consumes}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] consumes() default {};
+
+    /**
+     * Alias for {@link RequestMapping#produces}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] produces() default {};
+
+}

+ 91 - 0
eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPostMapping.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.zhengjie.annotation.rest;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import me.zhengjie.annotation.AnonymousAccess;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+/**
+ * Annotation for mapping HTTP {@code POST} requests onto specific handler
+ * methods.
+ * 支持匿名访问 PostMapping
+ *
+ * @author liaojinlong
+ * @see AnonymousGetMapping
+ * @see AnonymousPostMapping
+ * @see AnonymousPutMapping
+ * @see AnonymousDeleteMapping
+ * @see RequestMapping
+ */
+@AnonymousAccess
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RequestMapping(method = RequestMethod.POST)
+public @interface AnonymousPostMapping {
+
+    /**
+     * Alias for {@link RequestMapping#name}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String name() default "";
+
+    /**
+     * Alias for {@link RequestMapping#value}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] value() default {};
+
+    /**
+     * Alias for {@link RequestMapping#path}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] path() default {};
+
+    /**
+     * Alias for {@link RequestMapping#params}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] params() default {};
+
+    /**
+     * Alias for {@link RequestMapping#headers}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] headers() default {};
+
+    /**
+     * Alias for {@link RequestMapping#consumes}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] consumes() default {};
+
+    /**
+     * Alias for {@link RequestMapping#produces}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] produces() default {};
+
+}

+ 91 - 0
eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPutMapping.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.zhengjie.annotation.rest;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import me.zhengjie.annotation.AnonymousAccess;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+/**
+ * Annotation for mapping HTTP {@code PUT} requests onto specific handler
+ * methods.
+ * * 支持匿名访问  PutMapping
+ *
+ * @author liaojinlong
+ * @see AnonymousGetMapping
+ * @see AnonymousPostMapping
+ * @see AnonymousPutMapping
+ * @see AnonymousDeleteMapping
+ * @see RequestMapping
+ */
+@AnonymousAccess
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RequestMapping(method = RequestMethod.PUT)
+public @interface AnonymousPutMapping {
+
+    /**
+     * Alias for {@link RequestMapping#name}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String name() default "";
+
+    /**
+     * Alias for {@link RequestMapping#value}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] value() default {};
+
+    /**
+     * Alias for {@link RequestMapping#path}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] path() default {};
+
+    /**
+     * Alias for {@link RequestMapping#params}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] params() default {};
+
+    /**
+     * Alias for {@link RequestMapping#headers}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] headers() default {};
+
+    /**
+     * Alias for {@link RequestMapping#consumes}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] consumes() default {};
+
+    /**
+     * Alias for {@link RequestMapping#produces}.
+     */
+    @AliasFor(annotation = RequestMapping.class)
+    String[] produces() default {};
+
+}

+ 99 - 0
eladmin-common/src/main/java/me/zhengjie/aspect/LimitAspect.java

@@ -0,0 +1,99 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.aspect;
+
+import com.google.common.collect.ImmutableList;
+import me.zhengjie.annotation.Limit;
+import me.zhengjie.exception.BadRequestException;
+import me.zhengjie.utils.RequestHolder;
+import me.zhengjie.utils.StringUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+
+/**
+ * @author /
+ */
+@Aspect
+@Component
+public class LimitAspect {
+
+    private final RedisTemplate<Object,Object> redisTemplate;
+    private static final Logger logger = LoggerFactory.getLogger(LimitAspect.class);
+
+    public LimitAspect(RedisTemplate<Object,Object> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Pointcut("@annotation(me.zhengjie.annotation.Limit)")
+    public void pointcut() {
+    }
+
+    @Around("pointcut()")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        HttpServletRequest request = RequestHolder.getHttpServletRequest();
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method signatureMethod = signature.getMethod();
+        Limit limit = signatureMethod.getAnnotation(Limit.class);
+        LimitType limitType = limit.limitType();
+        String key = limit.key();
+        if (StringUtils.isEmpty(key)) {
+            if (limitType == LimitType.IP) {
+                key = StringUtils.getIp(request);
+            } else {
+                key = signatureMethod.getName();
+            }
+        }
+
+        ImmutableList<Object> keys = ImmutableList.of(StringUtils.join(limit.prefix(), "_", key, "_", request.getRequestURI().replaceAll("/","_")));
+
+        String luaScript = buildLuaScript();
+        RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
+        Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period());
+        if (null != count && count.intValue() <= limit.count()) {
+            logger.info("第{}次访问key为 {},描述为 [{}] 的接口", count, keys, limit.name());
+            return joinPoint.proceed();
+        } else {
+            throw new BadRequestException("访问次数受限制");
+        }
+    }
+
+    /**
+     * 限流脚本
+     */
+    private String buildLuaScript() {
+        return "local c" +
+                "\nc = redis.call('get',KEYS[1])" +
+                "\nif c and tonumber(c) > tonumber(ARGV[1]) then" +
+                "\nreturn c;" +
+                "\nend" +
+                "\nc = redis.call('incr',KEYS[1])" +
+                "\nif tonumber(c) == 1 then" +
+                "\nredis.call('expire',KEYS[1],ARGV[2])" +
+                "\nend" +
+                "\nreturn c;";
+    }
+}

+ 27 - 0
eladmin-common/src/main/java/me/zhengjie/aspect/LimitType.java

@@ -0,0 +1,27 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.aspect;
+
+/**
+ * 限流枚举
+ * @author /
+ */
+public enum LimitType {
+    // 默认
+    CUSTOMER,
+    //  by ip addr
+    IP
+}

+ 40 - 0
eladmin-common/src/main/java/me/zhengjie/base/BaseDTO.java

@@ -0,0 +1,40 @@
+package me.zhengjie.base;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.sql.Timestamp;
+
+/**
+ * @author Zheng Jie
+ * @date 2019年10月24日20:48:53
+ */
+@Getter
+@Setter
+public class BaseDTO  implements Serializable {
+
+    private String createBy;
+
+    private String updateBy;
+
+    private Timestamp createTime;
+
+    private Timestamp updateTime;
+
+    @Override
+    public String toString() {
+        ToStringBuilder builder = new ToStringBuilder(this);
+        Field[] fields = this.getClass().getDeclaredFields();
+        try {
+            for (Field f : fields) {
+                f.setAccessible(true);
+                builder.append(f.getName(), f.get(this)).append("\n");
+            }
+        } catch (Exception e) {
+            builder.append("toString builder encounter an error");
+        }
+        return builder.toString();
+    }
+}

+ 85 - 0
eladmin-common/src/main/java/me/zhengjie/base/BaseEntity.java

@@ -0,0 +1,85 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.base;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.hibernate.annotations.CreationTimestamp;
+import org.hibernate.annotations.UpdateTimestamp;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+import javax.persistence.Column;
+import javax.persistence.EntityListeners;
+import javax.persistence.MappedSuperclass;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.sql.Timestamp;
+
+/**
+ * 通用字段, is_del 根据需求自行添加
+ * @author Zheng Jie
+ * @Date 2019年10月24日20:46:32
+ */
+@Getter
+@Setter
+@MappedSuperclass
+@EntityListeners(AuditingEntityListener.class)
+public class BaseEntity implements Serializable {
+
+    @CreatedBy
+    @Column(name = "create_by", updatable = false)
+    @ApiModelProperty(value = "创建人", hidden = true)
+    private String createBy;
+
+    @LastModifiedBy
+    @Column(name = "update_by")
+    @ApiModelProperty(value = "更新人", hidden = true)
+    private String updateBy;
+
+    @CreationTimestamp
+    @Column(name = "create_time", updatable = false)
+    @ApiModelProperty(value = "创建时间", hidden = true)
+    private Timestamp createTime;
+
+    @UpdateTimestamp
+    @Column(name = "update_time")
+    @ApiModelProperty(value = "更新时间", hidden = true)
+    private Timestamp updateTime;
+
+    /* 分组校验 */
+    public @interface Create {}
+
+    /* 分组校验 */
+    public @interface Update {}
+
+    @Override
+    public String toString() {
+        ToStringBuilder builder = new ToStringBuilder(this);
+        Field[] fields = this.getClass().getDeclaredFields();
+        try {
+            for (Field f : fields) {
+                f.setAccessible(true);
+                builder.append(f.getName(), f.get(this)).append("\n");
+            }
+        } catch (Exception e) {
+            builder.append("toString builder encounter an error");
+        }
+        return builder.toString();
+    }
+}

+ 53 - 0
eladmin-common/src/main/java/me/zhengjie/base/BaseMapper.java

@@ -0,0 +1,53 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.base;
+
+import java.util.List;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-23
+ */
+public interface BaseMapper<D, E> {
+
+    /**
+     * DTO转Entity
+     * @param dto /
+     * @return /
+     */
+    E toEntity(D dto);
+
+    /**
+     * Entity转DTO
+     * @param entity /
+     * @return /
+     */
+    D toDto(E entity);
+
+    /**
+     * DTO集合转Entity集合
+     * @param dtoList /
+     * @return /
+     */
+    List <E> toEntity(List<D> dtoList);
+
+    /**
+     * Entity集合转DTO集合
+     * @param entityList /
+     * @return /
+     */
+    List <D> toDto(List<E> entityList);
+}

+ 64 - 0
eladmin-common/src/main/java/me/zhengjie/base/BaseResponse.java

@@ -0,0 +1,64 @@
+package me.zhengjie.base;
+
+import java.io.Serializable;
+
+public class BaseResponse<T>  implements Serializable {
+
+    private int status;
+    private String message;
+    private T data;
+    private Long timestamp;
+
+    public BaseResponse(T data) {
+        this.setStatus(200);
+        this.setData(data);
+        this.setMessage("sucess");
+        this.setTimestamp(System.currentTimeMillis());
+    }
+
+    public BaseResponse(T data, int code) {
+        this.setStatus(code);
+        this.setData(data);
+        this.setMessage("sucess");
+        this.setTimestamp(System.currentTimeMillis());
+    }
+
+    public BaseResponse(T data, int code, String message) {
+        this.setStatus(code);
+        this.setData(data);
+        this.setMessage(message);
+        this.setTimestamp(System.currentTimeMillis());
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public void setStatus(int status) {
+        this.status = status;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+
+    public Long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(Long timestamp) {
+        this.timestamp = timestamp;
+    }
+}

+ 33 - 0
eladmin-common/src/main/java/me/zhengjie/base/QueryPageParams.java

@@ -0,0 +1,33 @@
+package me.zhengjie.base;
+
+public class QueryPageParams<T> {
+    private String timestamp;
+    private String nonce;
+
+    public String getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(String timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public String getNonce() {
+        return nonce;
+    }
+
+    public void setNonce(String nonce) {
+        this.nonce = nonce;
+    }
+
+    private T query;
+
+    public T getQuery() {
+        return query;
+    }
+
+    public void setQuery(T query) {
+        this.query = query;
+    }
+
+}

+ 45 - 0
eladmin-common/src/main/java/me/zhengjie/config/AuditorConfig.java

@@ -0,0 +1,45 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.config;
+
+import me.zhengjie.utils.SecurityUtils;
+import org.springframework.data.domain.AuditorAware;
+import org.springframework.stereotype.Component;
+import java.util.Optional;
+
+/**
+ * @description  : 设置审计
+ * @author  : Dong ZhaoYang
+ * @date : 2019/10/28
+ */
+@Component("auditorAware")
+public class AuditorConfig implements AuditorAware<String> {
+
+    /**
+     * 返回操作员标志信息
+     *
+     * @return /
+     */
+    @Override
+    public Optional<String> getCurrentAuditor() {
+        try {
+            // 这里应根据实际业务情况获取具体信息
+            return Optional.of(SecurityUtils.getCurrentUsername());
+        }catch (Exception ignored){}
+        // 用户定时任务,或者无Token调用的情况
+        return Optional.of("System");
+    }
+}

+ 37 - 0
eladmin-common/src/main/java/me/zhengjie/config/ElPermissionConfig.java

@@ -0,0 +1,37 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.config;
+
+import me.zhengjie.utils.SecurityUtils;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.stereotype.Service;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Zheng Jie
+ */
+@Service(value = "el")
+public class ElPermissionConfig {
+
+    public Boolean check(String ...permissions){
+        // 获取当前用户的所有权限
+        List<String> elPermissions = SecurityUtils.getCurrentUser().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
+        // 判断当前用户的所有权限是否包含接口上定义的权限
+        return elPermissions.contains("admin") || Arrays.stream(permissions).anyMatch(elPermissions::contains);
+    }
+}

+ 60 - 0
eladmin-common/src/main/java/me/zhengjie/config/FileProperties.java

@@ -0,0 +1,60 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.config;
+
+import lombok.Data;
+import me.zhengjie.utils.ElAdminConstant;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Zheng Jie
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "file")
+public class FileProperties {
+
+    /** 文件大小限制 */
+    private Long maxSize;
+
+    /** 头像大小限制 */
+    private Long avatarMaxSize;
+
+    private ElPath mac;
+
+    private ElPath linux;
+
+    private ElPath windows;
+
+    public ElPath getPath(){
+        String os = System.getProperty("os.name");
+        if(os.toLowerCase().startsWith(ElAdminConstant.WIN)) {
+            return windows;
+        } else if(os.toLowerCase().startsWith(ElAdminConstant.MAC)){
+            return mac;
+        }
+        return linux;
+    }
+
+    @Data
+    public static class ElPath{
+
+        private String path;
+
+        private String avatar;
+    }
+}

+ 215 - 0
eladmin-common/src/main/java/me/zhengjie/config/RedisConfig.java

@@ -0,0 +1,215 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.config;
+
+import cn.hutool.core.lang.Assert;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.parser.ParserConfig;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import lombok.extern.slf4j.Slf4j;
+import me.zhengjie.utils.StringUtils;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cache.Cache;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.interceptor.CacheErrorHandler;
+import org.springframework.cache.interceptor.KeyGenerator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-24
+ */
+@Slf4j
+@Configuration
+@EnableCaching
+@ConditionalOnClass(RedisOperations.class)
+@EnableConfigurationProperties(RedisProperties.class)
+public class RedisConfig extends CachingConfigurerSupport {
+
+    /**
+     *  设置 redis 数据默认过期时间,默认2小时
+     *  设置@cacheable 序列化方式
+     */
+    @Bean
+    public RedisCacheConfiguration redisCacheConfiguration(){
+        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
+        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
+        configuration = configuration.serializeValuesWith(RedisSerializationContext.
+                SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(6));
+        return configuration;
+    }
+
+    @SuppressWarnings("all")
+    @Bean(name = "redisTemplate")
+    @ConditionalOnMissingBean(name = "redisTemplate")
+    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        RedisTemplate<Object, Object> template = new RedisTemplate<>();
+        //序列化
+        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
+        // value值的序列化采用fastJsonRedisSerializer
+        template.setValueSerializer(fastJsonRedisSerializer);
+        template.setHashValueSerializer(fastJsonRedisSerializer);
+        // 全局开启AutoType,这里方便开发,使用全局的方式
+        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
+        // 建议使用这种方式,小范围指定白名单
+        // ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain");
+        // key的序列化采用StringRedisSerializer
+        template.setKeySerializer(new StringRedisSerializer());
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setConnectionFactory(redisConnectionFactory);
+        return template;
+    }
+
+    /**
+     * 自定义缓存key生成策略,默认将使用该策略
+     */
+    @Bean
+    @Override
+    public KeyGenerator keyGenerator() {
+        return (target, method, params) -> {
+            Map<String,Object> container = new HashMap<>(3);
+            Class<?> targetClassClass = target.getClass();
+            // 类地址
+            container.put("class",targetClassClass.toGenericString());
+            // 方法名称
+            container.put("methodName",method.getName());
+            // 包名称
+            container.put("package",targetClassClass.getPackage());
+            // 参数列表
+            for (int i = 0; i < params.length; i++) {
+                container.put(String.valueOf(i),params[i]);
+            }
+            // 转为JSON字符串
+            String jsonString = JSON.toJSONString(container);
+            // 做SHA256 Hash计算,得到一个SHA256摘要作为Key
+            return DigestUtils.sha256Hex(jsonString);
+        };
+    }
+
+    @Bean
+    @Override
+    public CacheErrorHandler errorHandler() {
+        // 异常处理,当Redis发生异常时,打印日志,但是程序正常走
+        log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
+        return new CacheErrorHandler() {
+            @Override
+            public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
+                log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
+            }
+
+            @Override
+            public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
+                log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
+            }
+
+            @Override
+            public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
+                log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
+            }
+
+            @Override
+            public void handleCacheClearError(RuntimeException e, Cache cache) {
+                log.error("Redis occur handleCacheClearError:", e);
+            }
+        };
+    }
+
+}
+
+/**
+ * Value 序列化
+ *
+ * @author /
+ * @param <T>
+ */
+ class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
+
+    private final Class<T> clazz;
+
+    FastJsonRedisSerializer(Class<T> clazz) {
+        super();
+        this.clazz = clazz;
+    }
+
+    @Override
+    public byte[] serialize(T t) {
+        if (t == null) {
+            return new byte[0];
+        }
+        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8);
+    }
+
+    @Override
+    public T deserialize(byte[] bytes) {
+        if (bytes == null || bytes.length <= 0) {
+            return null;
+        }
+        String str = new String(bytes, StandardCharsets.UTF_8);
+        return JSON.parseObject(str, clazz);
+    }
+
+}
+
+/**
+ * 重写序列化器
+ *
+ * @author /
+ */
+class StringRedisSerializer implements RedisSerializer<Object> {
+
+    private final Charset charset;
+
+    StringRedisSerializer() {
+        this(StandardCharsets.UTF_8);
+    }
+
+    private StringRedisSerializer(Charset charset) {
+        Assert.notNull(charset, "Charset must not be null!");
+        this.charset = charset;
+    }
+
+    @Override
+    public String deserialize(byte[] bytes) {
+        return (bytes == null ? null : new String(bytes, charset));
+    }
+
+    @Override
+    public byte[] serialize(Object object) {
+        String string = JSON.toJSONString(object);
+        if (StringUtils.isBlank(string)) {
+            return null;
+        }
+        string = string.replace("\"", "");
+        return string.getBytes(charset);
+    }
+}

+ 38 - 0
eladmin-common/src/main/java/me/zhengjie/config/RsaProperties.java

@@ -0,0 +1,38 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Zheng Jie
+ * @website https://el-admin.vip
+ * @description
+ * @date 2020-05-18
+ **/
+@Data
+@Component
+public class RsaProperties {
+
+    public static String privateKey;
+
+    @Value("${rsa.private_key}")
+    public void setPrivateKey(String privateKey) {
+        RsaProperties.privateKey = privateKey;
+    }
+}

+ 148 - 0
eladmin-common/src/main/java/me/zhengjie/config/SwaggerConfig.java

@@ -0,0 +1,148 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.config;
+
+import com.fasterxml.classmate.TypeResolver;
+import com.google.common.base.Predicates;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.data.domain.Pageable;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.schema.AlternateTypeRule;
+import springfox.documentation.schema.AlternateTypeRuleConvention;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+import java.util.ArrayList;
+import java.util.List;
+import static com.google.common.collect.Lists.newArrayList;
+import static springfox.documentation.schema.AlternateTypeRules.newRule;
+
+/**
+ * api页面 /doc.html
+ * @author Zheng Jie
+ * @date 2018-11-23
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    @Value("${jwt.header}")
+    private String tokenHeader;
+
+    @Value("${swagger.enabled}")
+    private Boolean enabled;
+
+    @Bean
+    @SuppressWarnings("all")
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .enable(enabled)
+                .pathMapping("/")
+                .apiInfo(apiInfo())
+                .select()
+                .paths(Predicates.not(PathSelectors.regex("/error.*")))
+                .paths(PathSelectors.any())
+                .build()
+                //添加登陆认证
+                .securitySchemes(securitySchemes())
+                .securityContexts(securityContexts());
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .description("一个简单且易上手的 Spring boot 后台管理框架")
+                .title("EL-ADMIN 接口文档")
+                .version("2.6")
+                .build();
+    }
+
+    private List<SecurityScheme> securitySchemes() {
+        //设置请求头信息
+        List<SecurityScheme> securitySchemes = new ArrayList<>();
+        ApiKey apiKey = new ApiKey(tokenHeader, tokenHeader, "header");
+        securitySchemes.add(apiKey);
+        return securitySchemes;
+    }
+
+    private List<SecurityContext> securityContexts() {
+        //设置需要登录认证的路径
+        List<SecurityContext> securityContexts = new ArrayList<>();
+        // ^(?!auth).*$ 表示所有包含auth的接口不需要使用securitySchemes即不需要带token
+        // ^标识开始  ()里是一子表达式  ?!/auth表示匹配不是/auth的位置,匹配上则添加请求头,注意路径已/开头  .表示任意字符  *表示前面的字符匹配多次 $标识结束
+        securityContexts.add(getContextByPath());
+        return securityContexts;
+    }
+
+    private SecurityContext getContextByPath() {
+        return SecurityContext.builder()
+                .securityReferences(defaultAuth())
+                .forPaths(PathSelectors.regex("^(?!/auth).*$"))
+                .build();
+    }
+
+    private List<SecurityReference> defaultAuth() {
+        List<SecurityReference> securityReferences = new ArrayList<>();
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        securityReferences.add(new SecurityReference(tokenHeader, authorizationScopes));
+        return securityReferences;
+    }
+}
+
+/**
+ *  将Pageable转换展示在swagger中
+ */
+@Configuration
+class SwaggerDataConfig {
+
+    @Bean
+    public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) {
+        return new AlternateTypeRuleConvention() {
+            @Override
+            public int getOrder() {
+                return Ordered.HIGHEST_PRECEDENCE;
+            }
+
+            @Override
+            public List<AlternateTypeRule> rules() {
+                return newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class)));
+            }
+        };
+    }
+
+    @ApiModel
+    @Data
+    private static class Page {
+        @ApiModelProperty("页码 (0..N)")
+        private Integer page;
+
+        @ApiModelProperty("每页显示的数目")
+        private Integer size;
+
+        @ApiModelProperty("以下列格式排序标准:property[,asc | desc]。 默认排序顺序为升序。 支持多种排序条件:如:id,asc")
+        private List<String> sort;
+    }
+}

+ 98 - 0
eladmin-common/src/main/java/me/zhengjie/exception/BadConfigurationException.java

@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package me.zhengjie.exception;
+
+/**
+ * 统一关于错误配置信息 异常
+ *
+ * @author: liaojinlong
+ * @date: 2020/6/10 18:06
+ */
+public class BadConfigurationException extends RuntimeException {
+    /**
+     * Constructs a new runtime exception with {@code null} as its
+     * detail message.  The cause is not initialized, and may subsequently be
+     * initialized by a call to {@link #initCause}.
+     */
+    public BadConfigurationException() {
+        super();
+    }
+
+    /**
+     * Constructs a new runtime exception with the specified detail message.
+     * The cause is not initialized, and may subsequently be initialized by a
+     * call to {@link #initCause}.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public BadConfigurationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new runtime exception with the specified detail message and
+     * cause.  <p>Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this runtime exception's detail message.
+     *
+     * @param message the detail message (which is saved for later retrieval
+     *                by the {@link #getMessage()} method).
+     * @param cause   the cause (which is saved for later retrieval by the
+     *                {@link #getCause()} method).  (A {@code null} value is
+     *                permitted, and indicates that the cause is nonexistent or
+     *                unknown.)
+     * @since 1.4
+     */
+    public BadConfigurationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new runtime exception with the specified cause and a
+     * detail message of {@code (cause==null ? null : cause.toString())}
+     * (which typically contains the class and detail message of
+     * {@code cause}).  This constructor is useful for runtime exceptions
+     * that are little more than wrappers for other throwables.
+     *
+     * @param cause the cause (which is saved for later retrieval by the
+     *              {@link #getCause()} method).  (A {@code null} value is
+     *              permitted, and indicates that the cause is nonexistent or
+     *              unknown.)
+     * @since 1.4
+     */
+    public BadConfigurationException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a new runtime exception with the specified detail
+     * message, cause, suppression enabled or disabled, and writable
+     * stack trace enabled or disabled.
+     *
+     * @param message            the detail message.
+     * @param cause              the cause.  (A {@code null} value is permitted,
+     *                           and indicates that the cause is nonexistent or unknown.)
+     * @param enableSuppression  whether or not suppression is enabled
+     *                           or disabled
+     * @param writableStackTrace whether or not the stack trace should
+     *                           be writable
+     * @since 1.7
+     */
+    protected BadConfigurationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+}

+ 40 - 0
eladmin-common/src/main/java/me/zhengjie/exception/BadRequestException.java

@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-23
+ * 统一异常处理
+ */
+@Getter
+public class BadRequestException extends RuntimeException{
+
+    private Integer status = BAD_REQUEST.value();
+
+    public BadRequestException(String msg){
+        super(msg);
+    }
+
+    public BadRequestException(HttpStatus status,String msg){
+        super(msg);
+        this.status = status.value();
+    }
+}

+ 34 - 0
eladmin-common/src/main/java/me/zhengjie/exception/EntityExistException.java

@@ -0,0 +1,34 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.exception;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-23
+ */
+public class EntityExistException extends RuntimeException {
+
+    public EntityExistException(Class clazz, String field, String val) {
+        super(EntityExistException.generateMessage(clazz.getSimpleName(), field, val));
+    }
+
+    private static String generateMessage(String entity, String field, String val) {
+        return StringUtils.capitalize(entity)
+                + " with " + field + " "+ val + " existed";
+    }
+}

+ 34 - 0
eladmin-common/src/main/java/me/zhengjie/exception/EntityNotFoundException.java

@@ -0,0 +1,34 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.exception;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-23
+ */
+public class EntityNotFoundException extends RuntimeException {
+
+    public EntityNotFoundException(Class clazz, String field, String val) {
+        super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), field, val));
+    }
+
+    private static String generateMessage(String entity, String field, String val) {
+        return StringUtils.capitalize(entity)
+                + " with " + field + " "+ val + " does not exist";
+    }
+}

+ 52 - 0
eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java

@@ -0,0 +1,52 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.exception.handler;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-23
+ */
+@Data
+class ApiError {
+
+    private Integer status = 400;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime timestamp;
+    private String message;
+
+    private ApiError() {
+        timestamp = LocalDateTime.now();
+    }
+
+    public static ApiError error(String message){
+        ApiError apiError = new ApiError();
+        apiError.setMessage(message);
+        return apiError;
+    }
+
+    public static ApiError error(Integer status, String message){
+        ApiError apiError = new ApiError();
+        apiError.setStatus(status);
+        apiError.setMessage(message);
+        return apiError;
+    }
+}
+
+

+ 113 - 0
eladmin-common/src/main/java/me/zhengjie/exception/handler/GlobalExceptionHandler.java

@@ -0,0 +1,113 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.exception.handler;
+
+import lombok.extern.slf4j.Slf4j;
+import me.zhengjie.exception.BadRequestException;
+import me.zhengjie.exception.EntityExistException;
+import me.zhengjie.exception.EntityNotFoundException;
+import me.zhengjie.utils.ThrowableUtil;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import java.util.Objects;
+import static org.springframework.http.HttpStatus.*;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-23
+ */
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    /**
+     * 处理所有不可知的异常
+     */
+    @ExceptionHandler(Throwable.class)
+    public ResponseEntity<ApiError> handleException(Throwable e){
+        // 打印堆栈信息
+        log.error(ThrowableUtil.getStackTrace(e));
+        return buildResponseEntity(ApiError.error(e.getMessage()));
+    }
+
+    /**
+     * BadCredentialsException
+     */
+    @ExceptionHandler(BadCredentialsException.class)
+    public ResponseEntity<ApiError> badCredentialsException(BadCredentialsException e){
+        // 打印堆栈信息
+        String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage();
+        log.error(message);
+        return buildResponseEntity(ApiError.error(message));
+    }
+
+    /**
+     * 处理自定义异常
+     */
+	@ExceptionHandler(value = BadRequestException.class)
+	public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
+        // 打印堆栈信息
+        log.error(ThrowableUtil.getStackTrace(e));
+        return buildResponseEntity(ApiError.error(e.getStatus(),e.getMessage()));
+	}
+
+    /**
+     * 处理 EntityExist
+     */
+    @ExceptionHandler(value = EntityExistException.class)
+    public ResponseEntity<ApiError> entityExistException(EntityExistException e) {
+        // 打印堆栈信息
+        log.error(ThrowableUtil.getStackTrace(e));
+        return buildResponseEntity(ApiError.error(e.getMessage()));
+    }
+
+    /**
+     * 处理 EntityNotFound
+     */
+    @ExceptionHandler(value = EntityNotFoundException.class)
+    public ResponseEntity<ApiError> entityNotFoundException(EntityNotFoundException e) {
+        // 打印堆栈信息
+        log.error(ThrowableUtil.getStackTrace(e));
+        return buildResponseEntity(ApiError.error(NOT_FOUND.value(),e.getMessage()));
+    }
+
+    /**
+     * 处理所有接口数据验证异常
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public ResponseEntity<ApiError> handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
+        // 打印堆栈信息
+        log.error(ThrowableUtil.getStackTrace(e));
+        String[] str = Objects.requireNonNull(e.getBindingResult().getAllErrors().get(0).getCodes())[1].split("\\.");
+        String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
+        String msg = "不能为空";
+        if(msg.equals(message)){
+            message = str[1] + ":" + message;
+        }
+        return buildResponseEntity(ApiError.error(message));
+    }
+
+    /**
+     * 统一返回
+     */
+    private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
+        return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus()));
+    }
+}

+ 265 - 0
eladmin-common/src/main/java/me/zhengjie/utils/BeanMapUtils.java

@@ -0,0 +1,265 @@
+package me.zhengjie.utils;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.*;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.util.*;
+
+public class BeanMapUtils {
+
+    /**
+     * Map转实体
+     * @param type
+     * @param map
+     * @return
+     * @throws Exception
+     */
+    public static Object convertMap(Class type, Map map) throws Exception {
+        BeanInfo beanInfo = Introspector.getBeanInfo(type);
+        Object obj = type.newInstance();
+        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
+        for (PropertyDescriptor descriptor : propertyDescriptors) {
+            String propertyName = descriptor.getName();
+            if (map.containsKey(propertyName)) {
+                Object value = map.get(propertyName);
+                descriptor.getWriteMethod().invoke(obj, value);
+            }
+        }
+        return obj;
+    }
+
+    /**
+     * 实体类转Map共通方法
+     *
+     * @param bean 实体类
+     * @return Map
+     * @throws Exception
+     */
+    public static Map convertBean(Object bean) throws Exception {
+        Class type = bean.getClass();
+        Map returnMap = new HashMap();
+        BeanInfo beanInfo = Introspector.getBeanInfo(type);
+        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
+        for (PropertyDescriptor descriptor : propertyDescriptors) {
+            String propertyName = descriptor.getName();
+            if (!propertyName.equals("class")) {
+                Method readMethod = descriptor.getReadMethod();
+                Object result = readMethod.invoke(bean);
+                if (result != null) {
+                    returnMap.put(propertyName, result);
+                } else {
+                    returnMap.put(propertyName, "");
+                }
+            }
+        }
+        return returnMap;
+    }
+
+    /**
+        * 随机生成UUID
+        * @return
+        */
+    public static synchronized String getUUID(){
+        UUID uuid = UUID.randomUUID();
+        String str = uuid.toString();
+        return str;
+    }
+
+    /**
+     * 发送post请求(raw)
+     * @param url 接口地址
+     * @param json raw中json参数
+     * @return 请求结果
+     * @throws ClientProtocolException
+     * @throws IOException
+     */
+    public static synchronized String sendPost(String url,String json)throws ClientProtocolException, IOException {
+        HttpClient httpClient = new DefaultHttpClient();
+        HttpPost post = new HttpPost(url);
+        StringEntity postingString = new StringEntity(json);// json传递
+        post.setEntity(postingString);
+        post.setHeader("Content-type", "application/json");
+        HttpResponse response = httpClient.execute(post);
+        String content = EntityUtils.toString(response.getEntity());
+        return content;
+    }
+
+    /**
+     *
+     * @param httpUrl  请求的url
+     * @param param  form表单的参数(key,value形式)
+     * @return
+     */
+    public static String doPostForm(String httpUrl, Map param) {
+
+        HttpURLConnection connection = null;
+        InputStream is = null;
+        OutputStream os = null;
+        BufferedReader br = null;
+        String result = null;
+        try {
+            URL url = new URL(httpUrl);
+            // 通过远程url连接对象打开连接
+            connection = (HttpURLConnection) url.openConnection();
+            // 设置连接请求方式
+            connection.setRequestMethod("POST");
+            // 设置连接主机服务器超时时间:15000毫秒
+            connection.setConnectTimeout(15000);
+            // 设置读取主机服务器返回数据超时时间:60000毫秒
+            connection.setReadTimeout(60000);
+
+            // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
+            connection.setDoOutput(true);
+            // 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无
+            connection.setDoInput(true);
+            // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。
+            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            // 设置鉴权信息:Authorization: Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0
+            //connection.setRequestProperty("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0");
+            // 通过连接对象获取一个输出流
+            os = connection.getOutputStream();
+            // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的(form表单形式的参数实质也是key,value值的拼接,类似于get请求参数的拼接)
+            os.write(createLinkString(param).getBytes());
+            // 通过连接对象获取一个输入流,向远程读取
+            if (connection.getResponseCode() == 200) {
+
+                is = connection.getInputStream();
+                // 对输入流对象进行包装:charset根据工作项目组的要求来设置
+                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+
+                StringBuffer sbf = new StringBuffer();
+                String temp = null;
+                // 循环遍历一行一行读取数据
+                while ((temp = br.readLine()) != null) {
+                    sbf.append(temp);
+                    sbf.append("\r\n");
+                }
+                result = sbf.toString();
+            }
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            // 关闭资源
+            if (null != br) {
+                try {
+                    br.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (null != os) {
+                try {
+                    os.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (null != is) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            // 断开与远程地址url的连接
+            connection.disconnect();
+        }
+        return result;
+    }
+
+    /**
+     * 向指定URL发送GET方法的请求
+     *
+     * @param url
+     *            发送请求的URL
+     * @param param
+     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return URL 所代表远程资源的响应结果
+     */
+    public static String sendGet(String url, String param) {
+        String result = "";
+        BufferedReader in = null;
+        try {
+            String urlNameString = url + "?" + param;
+            URL realUrl = new URL(urlNameString);
+            // 打开和URL之间的连接
+            URLConnection connection = realUrl.openConnection();
+            // 设置通用的请求属性
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("user-agent",
+                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            // 建立实际的连接
+            connection.connect();
+            // 获取所有响应头字段
+            Map<String, List<String>> map = connection.getHeaderFields();
+            // 遍历所有的响应头字段
+            for (String key : map.keySet()) {
+                System.out.println(key + "--->" + map.get(key));
+            }
+            // 定义 BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(new InputStreamReader(
+                    connection.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+            }
+        } catch (Exception e) {
+            System.out.println("发送GET请求出现异常!" + e);
+            e.printStackTrace();
+        }
+        // 使用finally块来关闭输入流
+        finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (Exception e2) {
+                e2.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
+     * @param params 需要排序并参与字符拼接的参数组
+     * @return 拼接后字符串
+     */
+    public static String createLinkString(Map<String, String> params) {
+
+        List<String> keys = new ArrayList<String>(params.keySet());
+        Collections.sort(keys);
+
+        StringBuilder prestr = new StringBuilder();
+        for (int i = 0; i < keys.size(); i++) {
+            String key = keys.get(i);
+            String value = params.get(key);
+            if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
+                prestr.append(key).append("=").append(value);
+            } else {
+                prestr.append(key).append("=").append(value).append("&");
+            }
+        }
+
+        return prestr.toString();
+    }
+
+}

+ 58 - 0
eladmin-common/src/main/java/me/zhengjie/utils/CacheKey.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package me.zhengjie.utils;
+
+/**
+ * @author: liaojinlong
+ * @date: 2020/6/11 15:49
+ * @apiNote: 关于缓存的Key集合
+ */
+public interface CacheKey {
+
+    /**
+     * 用户
+     */
+    String USER_ID = "user::id:";
+    /**
+     * 数据
+     */
+    String DATA_USER = "data::user:";
+    /**
+     * 菜单
+     */
+    String MENU_ID = "menu::id:";
+    String MENU_USER = "menu::user:";
+    /**
+     * 角色授权
+     */
+    String ROLE_AUTH = "role::auth:";
+    /**
+     * 角色信息
+     */
+    String ROLE_ID = "role::id:";
+    /**
+     * 部门
+     */
+    String DEPT_ID = "dept::id:";
+    /**
+     * 岗位
+     */
+    String JOB_ID = "job::id:";
+    /**
+     * 数据字典
+     */
+    String DICT_NAME = "dict::name:";
+}

+ 43 - 0
eladmin-common/src/main/java/me/zhengjie/utils/CallBack.java

@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019-2020  the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.zhengjie.utils;
+
+/**
+ * @author: liaojinlong
+ * @date: 2020/6/9 17:02
+ * @since: 1.0
+ * @see {@link SpringContextHolder}
+ * 针对某些初始化方法,在SpringContextHolder 初始化前时,<br>
+ * 可提交一个 提交回调任务。<br>
+ * 在SpringContextHolder 初始化后,进行回调使用
+ */
+
+public interface CallBack {
+    /**
+     * 回调执行方法
+     */
+    void executor();
+
+    /**
+     * 本回调任务名称
+     * @return /
+     */
+    default String getCallBackName() {
+        return Thread.currentThread().getId() + ":" + this.getClass().getName();
+    }
+}
+

+ 47 - 0
eladmin-common/src/main/java/me/zhengjie/utils/CloseUtil.java

@@ -0,0 +1,47 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import java.io.Closeable;
+
+/**
+ * @author Zheng Jie
+ * @website https://el-admin.vip
+ * @description 用于关闭各种连接,缺啥补啥
+ * @date 2021-03-05
+ **/
+public class CloseUtil {
+
+    public static void close(Closeable closeable) {
+        if (null != closeable) {
+            try {
+                closeable.close();
+            } catch (Exception e) {
+                // 静默关闭
+            }
+        }
+    }
+
+    public static void close(AutoCloseable closeable) {
+        if (null != closeable) {
+            try {
+                closeable.close();
+            } catch (Exception e) {
+                // 静默关闭
+            }
+        }
+    }
+}

+ 40 - 0
eladmin-common/src/main/java/me/zhengjie/utils/ConnectionUtil.java

@@ -0,0 +1,40 @@
+package me.zhengjie.utils;
+
+import com.rabbitmq.client.Connection;
+import com.rabbitmq.client.ConnectionFactory;
+
+import java.io.IOException;
+
+
+/**
+ * @author wangmx
+ * 连接 RabbitMq  工具类
+ */
+public class ConnectionUtil {
+
+    public static Connection getConnection() {
+        //定义连接工厂
+        ConnectionFactory factory = new ConnectionFactory();
+        //设置服务地址
+        factory.setHost("127.0.0.1");
+        //端口
+        factory.setPort(5672);
+        //设置账号信息
+        //vhost(Virtual Host)
+//        factory.setVirtualHost("test");
+        //用户名
+        factory.setUsername("admin");
+        //密码
+        factory.setPassword("admin");
+        //通过工厂获得与mq的连接对象
+        Connection connection = null;
+        try {
+            connection = factory.newConnection();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return connection;
+    }
+
+}
+

+ 206 - 0
eladmin-common/src/main/java/me/zhengjie/utils/DateUtil.java

@@ -0,0 +1,206 @@
+/*
+ * Copyright 2019-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.zhengjie.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author: liaojinlong
+ * @date: 2020/6/11 16:28
+ * @apiNote: JDK 8  新日期类 格式化与字符串转换 工具类
+ */
+public class DateUtil {
+
+    public static final DateTimeFormatter DFY_MD_HMS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    public static final DateTimeFormatter DFY_MD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+    public static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    /**
+     * LocalDateTime 转时间戳
+     *
+     * @param localDateTime /
+     * @return /
+     */
+    public static Long getTimeStamp(LocalDateTime localDateTime) {
+        return localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond();
+    }
+
+    /**
+     * 时间戳转LocalDateTime
+     *
+     * @param timeStamp /
+     * @return /
+     */
+    public static LocalDateTime fromTimeStamp(Long timeStamp) {
+        return LocalDateTime.ofEpochSecond(timeStamp, 0, OffsetDateTime.now().getOffset());
+    }
+
+    /**
+     * LocalDateTime 转 Date
+     * Jdk8 后 不推荐使用 {@link Date} Date
+     *
+     * @param localDateTime /
+     * @return /
+     */
+    public static Date toDate(LocalDateTime localDateTime) {
+        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * LocalDate 转 Date
+     * Jdk8 后 不推荐使用 {@link Date} Date
+     *
+     * @param localDate /
+     * @return /
+     */
+    public static Date toDate(LocalDate localDate) {
+        return toDate(localDate.atTime(LocalTime.now(ZoneId.systemDefault())));
+    }
+
+
+    /**
+     * Date转 LocalDateTime
+     * Jdk8 后 不推荐使用 {@link Date} Date
+     *
+     * @param date /
+     * @return /
+     */
+    public static LocalDateTime toLocalDateTime(Date date) {
+        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
+    }
+
+    /**
+     * 日期 格式化
+     *
+     * @param localDateTime /
+     * @param patten /
+     * @return /
+     */
+    public static String localDateTimeFormat(LocalDateTime localDateTime, String patten) {
+        DateTimeFormatter df = DateTimeFormatter.ofPattern(patten);
+        return df.format(localDateTime);
+    }
+
+    /**
+     * 日期 格式化
+     *
+     * @param localDateTime /
+     * @param df /
+     * @return /
+     */
+    public static String localDateTimeFormat(LocalDateTime localDateTime, DateTimeFormatter df) {
+        return df.format(localDateTime);
+    }
+
+    /**
+     * 日期格式化 yyyy-MM-dd HH:mm:ss
+     *
+     * @param localDateTime /
+     * @return /
+     */
+    public static String localDateTimeFormatyMdHms(LocalDateTime localDateTime) {
+        return DFY_MD_HMS.format(localDateTime);
+    }
+
+    /**
+     * 日期格式化 yyyy-MM-dd
+     *
+     * @param localDateTime /
+     * @return /
+     */
+    public String localDateTimeFormatyMd(LocalDateTime localDateTime) {
+        return DFY_MD.format(localDateTime);
+    }
+
+    /**
+     * 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd
+     *
+     * @param localDateTime /
+     * @return /
+     */
+    public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, String pattern) {
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
+        return LocalDateTime.from(dateTimeFormatter.parse(localDateTime));
+    }
+
+    /**
+     * 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd
+     *
+     * @param localDateTime /
+     * @return /
+     */
+    public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, DateTimeFormatter dateTimeFormatter) {
+        return LocalDateTime.from(dateTimeFormatter.parse(localDateTime));
+    }
+
+    /**
+     * 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd HH:mm:ss
+     *
+     * @param localDateTime /
+     * @return /
+     */
+    public static LocalDateTime parseLocalDateTimeFormatyMdHms(String localDateTime) {
+        return LocalDateTime.from(DFY_MD_HMS.parse(localDateTime));
+    }
+
+    /**
+     *
+     * @param startDate 开始时间
+     * @param endDate  结束时间
+     * @return 相差多少天
+     * @throws ParseException
+     */
+    public static int daysBetween(Date startDate,Date endDate) throws ParseException {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+//        Calendar calendar = Calendar.getInstance();
+//        calendar.setTime(sdf.parse(sdf.format(startDate)));
+//        long start = calendar.getTimeInMillis();
+//        calendar.setTime(sdf.parse(sdf.format(endDate)));
+//        long end = calendar.getTimeInMillis();
+//        long betweendays=(end-start)/(1000*3600*24);
+//        int days = Integer.parseInt(String.valueOf(betweendays));
+//        return days;
+
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(sdf.parse(sdf.format(startDate)));
+        long time1 = cal.getTimeInMillis();
+        cal.setTime(sdf.parse(sdf.format(endDate)));
+        long time2 = cal.getTimeInMillis();
+        long between_days=(time2-time1)/(1000*3600*24);
+        if (time2>time1){
+            between_days = between_days+1;
+        }
+        return Integer.parseInt(String.valueOf(between_days));
+    }
+
+    /**
+     * Timestamp转日期 ,字符串格式 yyyy-MM-dd HH:mm:ss
+     *
+     * @param timestamp /
+     * @return /
+     */
+    public static String parseTimestampFormatyMdHms(Timestamp timestamp) {
+        return df.format(timestamp);
+    }
+}

+ 47 - 0
eladmin-common/src/main/java/me/zhengjie/utils/ElAdminConstant.java

@@ -0,0 +1,47 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+/**
+ * 常用静态常量
+ *
+ * @author Zheng Jie
+ * @date 2018-12-26
+ */
+public class ElAdminConstant {
+
+    /**
+     * 用于IP定位转换
+     */
+    public static final String REGION = "内网IP|内网IP";
+    /**
+     * win 系统
+     */
+    public static final String WIN = "win";
+
+    /**
+     * mac 系统
+     */
+    public static final String MAC = "mac";
+
+    /**
+     * 常用接口
+     */
+    public static class Url {
+        // IP归属地查询
+        public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true";
+    }
+}

+ 127 - 0
eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java

@@ -0,0 +1,127 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.DESKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
+/**
+ * 加密
+ * @author Zheng Jie
+ * @date 2018-11-23
+ */
+
+public class EncryptUtils {
+
+    private static final String STR_PARAM = "Passw0rd";
+
+    private static Cipher cipher;
+
+    private static final IvParameterSpec IV = new IvParameterSpec(STR_PARAM.getBytes(StandardCharsets.UTF_8));
+
+    private static DESKeySpec getDesKeySpec(String source) throws Exception {
+        if (source == null || source.length() == 0){
+            return null;
+        }
+        cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
+        String strKey = "Passw0rd";
+        return new DESKeySpec(strKey.getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * 对称加密
+     */
+    public static String desEncrypt(String source) throws Exception {
+        DESKeySpec desKeySpec = getDesKeySpec(source);
+        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
+        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
+        cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV);
+        return byte2hex(
+                cipher.doFinal(source.getBytes(StandardCharsets.UTF_8))).toUpperCase();
+    }
+
+    /**
+     * 对称解密
+     */
+    public static String desDecrypt(String source) throws Exception {
+        byte[] src = hex2byte(source.getBytes(StandardCharsets.UTF_8));
+        DESKeySpec desKeySpec = getDesKeySpec(source);
+        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
+        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
+        cipher.init(Cipher.DECRYPT_MODE, secretKey, IV);
+        byte[] retByte = cipher.doFinal(src);
+        return new String(retByte);
+    }
+
+    private static String byte2hex(byte[] inStr) {
+        String stmp;
+        StringBuilder out = new StringBuilder(inStr.length * 2);
+        for (byte b : inStr) {
+            stmp = Integer.toHexString(b & 0xFF);
+            if (stmp.length() == 1) {
+                // 如果是0至F的单位字符串,则添加0
+                out.append("0").append(stmp);
+            } else {
+                out.append(stmp);
+            }
+        }
+        return out.toString();
+    }
+
+    private static byte[] hex2byte(byte[] b) {
+        int size = 2;
+        if ((b.length % size) != 0){
+            throw new IllegalArgumentException("长度不是偶数");
+        }
+        byte[] b2 = new byte[b.length / 2];
+        for (int n = 0; n < b.length; n += size) {
+            String item = new String(b, n, 2);
+            b2[n / 2] = (byte) Integer.parseInt(item, 16);
+        }
+        return b2;
+    }
+
+    /**
+     * @return
+     * @Comment SHA1 加密
+     */
+    public static String sha1(String str) {
+        MessageDigest sha = null;
+        try {
+            sha = MessageDigest.getInstance("SHA");
+            byte[] byteArray = str.getBytes("UTF-8");
+            byte[] md5Bytes = sha.digest(byteArray);
+            StringBuffer hexValue = new StringBuffer();
+            for (int i = 0; i < md5Bytes.length; i++) {
+                int val = ((int) md5Bytes[i]) & 0xff;
+                if (val < 16) {
+                    hexValue.append("0");
+                }
+                hexValue.append(Integer.toHexString(val));
+            }
+            return hexValue.toString();
+        } catch (Exception e) {
+            System.out.println(e.toString());
+            e.printStackTrace();
+            return "";
+        }
+    }
+}

+ 356 - 0
eladmin-common/src/main/java/me/zhengjie/utils/FileUtil.java

@@ -0,0 +1,356 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.poi.excel.BigExcelWriter;
+import cn.hutool.poi.excel.ExcelUtil;
+import me.zhengjie.exception.BadRequestException;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.xssf.streaming.SXSSFSheet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.security.MessageDigest;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * File工具类,扩展 hutool 工具包
+ *
+ * @author Zheng Jie
+ * @date 2018-12-27
+ */
+public class FileUtil extends cn.hutool.core.io.FileUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
+
+    /**
+     * 系统临时目录
+     * <br>
+     * windows 包含路径分割符,但Linux 不包含,
+     * 在windows \\==\ 前提下,
+     * 为安全起见 同意拼装 路径分割符,
+     * <pre>
+     *       java.io.tmpdir
+     *       windows : C:\Users/xxx\AppData\Local\Temp\
+     *       linux: /temp
+     * </pre>
+     */
+    public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator;
+    /**
+     * 定义GB的计算常量
+     */
+    private static final int GB = 1024 * 1024 * 1024;
+    /**
+     * 定义MB的计算常量
+     */
+    private static final int MB = 1024 * 1024;
+    /**
+     * 定义KB的计算常量
+     */
+    private static final int KB = 1024;
+
+    /**
+     * 格式化小数
+     */
+    private static final DecimalFormat DF = new DecimalFormat("0.00");
+
+    public static final String IMAGE = "图片";
+    public static final String TXT = "文档";
+    public static final String MUSIC = "音乐";
+    public static final String VIDEO = "视频";
+    public static final String OTHER = "其他";
+
+
+    /**
+     * MultipartFile转File
+     */
+    public static File toFile(MultipartFile multipartFile) {
+        // 获取文件名
+        String fileName = multipartFile.getOriginalFilename();
+        // 获取文件后缀
+        String prefix = "." + getExtensionName(fileName);
+        File file = null;
+        try {
+            // 用uuid作为文件名,防止生成的临时文件重复
+            file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + prefix);
+            // MultipartFile to File
+            multipartFile.transferTo(file);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+        return file;
+    }
+
+    /**
+     * 获取文件扩展名,不带 .
+     */
+    public static String getExtensionName(String filename) {
+        if ((filename != null) && (filename.length() > 0)) {
+            int dot = filename.lastIndexOf('.');
+            if ((dot > -1) && (dot < (filename.length() - 1))) {
+                return filename.substring(dot + 1);
+            }
+        }
+        return filename;
+    }
+
+    /**
+     * Java文件操作 获取不带扩展名的文件名
+     */
+    public static String getFileNameNoEx(String filename) {
+        if ((filename != null) && (filename.length() > 0)) {
+            int dot = filename.lastIndexOf('.');
+            if ((dot > -1) && (dot < (filename.length()))) {
+                return filename.substring(0, dot);
+            }
+        }
+        return filename;
+    }
+
+    /**
+     * 文件大小转换
+     */
+    public static String getSize(long size) {
+        String resultSize;
+        if (size / GB >= 1) {
+            //如果当前Byte的值大于等于1GB
+            resultSize = DF.format(size / (float) GB) + "GB   ";
+        } else if (size / MB >= 1) {
+            //如果当前Byte的值大于等于1MB
+            resultSize = DF.format(size / (float) MB) + "MB   ";
+        } else if (size / KB >= 1) {
+            //如果当前Byte的值大于等于1KB
+            resultSize = DF.format(size / (float) KB) + "KB   ";
+        } else {
+            resultSize = size + "B   ";
+        }
+        return resultSize;
+    }
+
+    /**
+     * inputStream 转 File
+     */
+    static File inputStreamToFile(InputStream ins, String name){
+        File file = new File(SYS_TEM_DIR + name);
+        if (file.exists()) {
+            return file;
+        }
+        OutputStream os = null;
+        try {
+            os = new FileOutputStream(file);
+            int bytesRead;
+            int len = 8192;
+            byte[] buffer = new byte[len];
+            while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
+                os.write(buffer, 0, bytesRead);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            CloseUtil.close(os);
+            CloseUtil.close(ins);
+        }
+        return file;
+    }
+
+    /**
+     * 将文件名解析成文件的上传路径
+     */
+    public static File upload(MultipartFile file, String filePath) {
+        Date date = new Date();
+        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS");
+        String name = getFileNameNoEx(file.getOriginalFilename());
+        String suffix = getExtensionName(file.getOriginalFilename());
+        String nowStr = "-" + format.format(date);
+        try {
+            String fileName = name + nowStr + "." + suffix;
+            String path = filePath + fileName;
+            // getCanonicalFile 可解析正确各种路径
+            File dest = new File(path).getCanonicalFile();
+            // 检测是否存在目录
+            if (!dest.getParentFile().exists()) {
+                if (!dest.getParentFile().mkdirs()) {
+                    System.out.println("was not successful.");
+                }
+            }
+            // 文件写入
+            file.transferTo(dest);
+            return dest;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * 导出excel
+     */
+    public static void downloadExcel(List<Map<String, Object>> list, HttpServletResponse response) throws IOException {
+        String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx";
+        File file = new File(tempPath);
+        BigExcelWriter writer = ExcelUtil.getBigWriter(file);
+        // 一次性写出内容,使用默认样式,强制输出标题
+        writer.write(list, true);
+        SXSSFSheet sheet = (SXSSFSheet)writer.getSheet();
+        //上面需要强转SXSSFSheet  不然没有trackAllColumnsForAutoSizing方法
+        sheet.trackAllColumnsForAutoSizing();
+        //列宽自适应
+        writer.autoSizeColumnAll();
+        //response为HttpServletResponse对象
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
+        //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
+        response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
+        ServletOutputStream out = response.getOutputStream();
+        // 终止后删除临时文件
+        file.deleteOnExit();
+        writer.flush(out, true);
+        //此处记得关闭输出Servlet流
+        IoUtil.close(out);
+    }
+
+    public static String getFileType(String type) {
+        String documents = "txt doc pdf ppt pps xlsx xls docx";
+        String music = "mp3 wav wma mpa ram ra aac aif m4a";
+        String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
+        String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
+        if (image.contains(type)) {
+            return IMAGE;
+        } else if (documents.contains(type)) {
+            return TXT;
+        } else if (music.contains(type)) {
+            return MUSIC;
+        } else if (video.contains(type)) {
+            return VIDEO;
+        } else {
+            return OTHER;
+        }
+    }
+
+    public static void checkSize(long maxSize, long size) {
+        // 1M
+        int len = 1024 * 1024;
+        if (size > (maxSize * len)) {
+            throw new BadRequestException("文件超出规定大小");
+        }
+    }
+
+    /**
+     * 判断两个文件是否相同
+     */
+    public static boolean check(File file1, File file2) {
+        String img1Md5 = getMd5(file1);
+        String img2Md5 = getMd5(file2);
+        if(img1Md5 != null){
+            return img1Md5.equals(img2Md5);
+        }
+        return false;
+    }
+
+    /**
+     * 判断两个文件是否相同
+     */
+    public static boolean check(String file1Md5, String file2Md5) {
+        return file1Md5.equals(file2Md5);
+    }
+
+    private static byte[] getByte(File file) {
+        // 得到文件长度
+        byte[] b = new byte[(int) file.length()];
+        InputStream in = null;
+        try {
+            in = new FileInputStream(file);
+            try {
+                System.out.println(in.read(b));
+            } catch (IOException e) {
+                log.error(e.getMessage(), e);
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return null;
+        } finally {
+            CloseUtil.close(in);
+        }
+        return b;
+    }
+
+    private static String getMd5(byte[] bytes) {
+        // 16进制字符
+        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+        try {
+            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
+            mdTemp.update(bytes);
+            byte[] md = mdTemp.digest();
+            int j = md.length;
+            char[] str = new char[j * 2];
+            int k = 0;
+            // 移位 输出字符串
+            for (byte byte0 : md) {
+                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
+                str[k++] = hexDigits[byte0 & 0xf];
+            }
+            return new String(str);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * 下载文件
+     *
+     * @param request  /
+     * @param response /
+     * @param file     /
+     */
+    public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) {
+        response.setCharacterEncoding(request.getCharacterEncoding());
+        response.setContentType("application/octet-stream");
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(file);
+            response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
+            IOUtils.copy(fis, response.getOutputStream());
+            response.flushBuffer();
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                    if (deleteOnExit) {
+                        file.deleteOnExit();
+                    }
+                } catch (IOException e) {
+                    log.error(e.getMessage(), e);
+                }
+            }
+        }
+    }
+
+    public static String getMd5(File file) {
+        return getMd5(getByte(file));
+    }
+}

+ 117 - 0
eladmin-common/src/main/java/me/zhengjie/utils/ImageUtil.java

@@ -0,0 +1,117 @@
+package me.zhengjie.utils;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import sun.misc.BASE64Encoder;
+
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import javax.imageio.ImageIO;
+import java.io.File;
+import com.google.zxing.common.BitMatrix;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ImageUtil {
+
+    //生成二维码颜色
+    private static final int BLACK = 0xFF000000;
+    //生成二维码颜色
+    private static final int WHITE = 0xFFFFFFFF;
+
+    /**
+     * 将网络图片编码为base64
+     *
+     * @param url
+     * @return
+     * @throws Exception
+     */
+    public static String encodeImageToBase64(URL url) throws Exception {
+        //将图片文件转化为字节数组字符串,并对其进行Base64编码处理
+        System.out.println("图片的路径为:" + url.toString());
+        //打开链接
+        HttpURLConnection conn = null;
+        try {
+            conn = (HttpURLConnection) url.openConnection();
+            //设置请求方式为"GET"
+            conn.setRequestMethod("GET");
+            //超时响应时间为5秒
+            conn.setConnectTimeout(5 * 1000);
+            //通过输入流获取图片数据
+            InputStream inStream = conn.getInputStream();
+            //得到图片的二进制数据,以二进制封装得到数据,具有通用性
+            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+            //创建一个Buffer字符串
+            byte[] buffer = new byte[1024];
+            //每次读取的字符串长度,如果为-1,代表全部读取完毕
+            int len = 0;
+            //使用一个输入流从buffer里把数据读取出来
+            while ((len = inStream.read(buffer)) != -1) {
+            //用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度
+                outStream.write(buffer, 0, len);
+            }
+            //关闭输入流
+            inStream.close();
+            byte[] data = outStream.toByteArray();
+            //对字节数组Base64编码
+            BASE64Encoder encoder = new BASE64Encoder();
+            String base64 = encoder.encode(data);
+            System.out.println("网络文件[{}]编码成base64字符串:[{}]"+url.toString()+base64);
+            return base64;//返回Base64编码过的字节数组字符串
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new Exception("图片解析失败,请联系客服!");
+        }
+    }
+
+    /**
+     * 生成二维码
+     * @param text    二维码内容
+     * @param width    二维码宽
+     * @param height    二维码高
+     * @param outPutPath    二维码生成保存路径
+     * @param imageType     二维码生成格式
+     */
+    public static String CodeCreate(String text, int width, int height, String outPutPath, String imageType){
+        Map<EncodeHintType, String> his = new HashMap<EncodeHintType, String>();
+        //设置编码字符集
+        his.put(EncodeHintType.CHARACTER_SET, "utf-8");
+        try {
+            //生成二维码
+            BitMatrix encode = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, his);
+            //获取到二维码宽高
+            int codeWidth = encode.getWidth();
+            int codeHeight = encode.getHeight();
+            //将二维码放入缓冲流中
+            BufferedImage image = new BufferedImage(codeWidth, codeHeight, BufferedImage.TYPE_INT_RGB);
+            for (int i = 0; i < codeWidth; i++) {
+                for (int j = 0; j < codeHeight; j++) {
+                    //循环将二维码内容套入图片
+                    image.setRGB(i, j, encode.get(i, j) ? BLACK : WHITE);
+                }
+            }
+            File outPutImage = new File(outPutPath);
+            //如果图片不存在创建图片
+            if(!outPutImage.exists()) {
+                outPutImage.createNewFile();
+            }
+            //将二维码写入图片
+            ImageIO.write(image, imageType, outPutImage);
+            String fileName = outPutImage.getName();
+            String url = "https://smartpark.caih.com/static/yktres/"+fileName;
+            return url;
+        } catch (WriterException e) {
+            e.printStackTrace();
+            return null;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+}

+ 54 - 0
eladmin-common/src/main/java/me/zhengjie/utils/MessageSendUtils.java

@@ -0,0 +1,54 @@
+package me.zhengjie.utils;
+
+import cn.hutool.http.HttpRequest;
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MessageSendUtils {
+
+    /**
+     * 发送IM信息
+     * @param sendId    子系统发送的关联id
+     * @param sendUserId    发送userId
+     * @param text    发送的内容
+     * @param usernames     接收信息者账号 多个以,隔开
+     */
+    public static void sendChatMessage(String sendId, String sendUserId, String text, String usernames){
+        Map<String,Object> token = SecurityUtils.getToken("DMERPYT!@#$QWER2021+{:>");
+        JSONObject query = new JSONObject();
+        query.put("systemName","一卡通系统");
+        query.put("sendId",sendId);//taskId
+        query.put("sendUserId",sendUserId);
+        JSONObject sendContentJSON = new JSONObject();
+        sendContentJSON.put("text",text);
+        query.put("sendContent",sendContentJSON);
+        query.put("usernames",usernames);
+        JSONObject param = new JSONObject();
+        param.put("nonce",token.get("nonce"));
+        param.put("timestamp",token.get("timestamp"));
+        param.put("query",query);
+        HttpRequest.get("https://smartpark.caih.com/zkxt/api/thirdparty/v1/message/sendChatMessage").header("XYTACCESSTOKEN", token.get("token").toString()).body(param.toJSONString()).execute().body();
+    }
+
+    /**
+     * 发送IM信息
+     * @param phone    接收手机号
+     * @param context    发送内容
+     */
+    public static void sendMsg(String phone, String context){
+        Map<String,Object> token = SecurityUtils.getToken("DMERPYT!@#$QWER2021+{:>");
+        //短信通知
+        String msgUrl = "https://smartpark.caih.com/zkxt/api/thirdparty/v1/message/sendMsg";
+        JSONObject query = new JSONObject();
+        query.put("phone",phone);
+        query.put("context",context);
+        JSONObject param = new JSONObject();
+        param.put("nonce",token.get("nonce"));
+        param.put("timestamp",token.get("timestamp"));
+        param.put("query",query);
+        HttpRequest.get(msgUrl).header("XYTACCESSTOKEN", token.get("token").toString()).body(param.toJSONString()).execute().body();
+    }
+
+}

+ 63 - 0
eladmin-common/src/main/java/me/zhengjie/utils/PageUtil.java

@@ -0,0 +1,63 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import org.springframework.data.domain.Page;
+import java.util.*;
+
+/**
+ * 分页工具
+ * @author Zheng Jie
+ * @date 2018-12-10
+ */
+public class PageUtil extends cn.hutool.core.util.PageUtil {
+
+    /**
+     * List 分页
+     */
+    public static List toPage(int page, int size , List list) {
+        int fromIndex = page * size;
+        int toIndex = page * size + size;
+        if(fromIndex > list.size()){
+            return new ArrayList();
+        } else if(toIndex >= list.size()) {
+            return list.subList(fromIndex,list.size());
+        } else {
+            return list.subList(fromIndex,toIndex);
+        }
+    }
+
+    /**
+     * Page 数据处理,预防redis反序列化报错
+     */
+    public static Map<String,Object> toPage(Page page) {
+        Map<String,Object> map = new LinkedHashMap<>(2);
+        map.put("content",page.getContent());
+        map.put("totalElements",page.getTotalElements());
+        return map;
+    }
+
+    /**
+     * 自定义分页
+     */
+    public static Map<String,Object> toPage(Object object, Object totalElements) {
+        Map<String,Object> map = new LinkedHashMap<>(2);
+        map.put("content",object);
+        map.put("totalElements",totalElements);
+        return map;
+    }
+
+}

+ 208 - 0
eladmin-common/src/main/java/me/zhengjie/utils/QueryHelp.java

@@ -0,0 +1,208 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjectUtil;
+import lombok.extern.slf4j.Slf4j;
+import me.zhengjie.annotation.DataPermission;
+import me.zhengjie.annotation.Query;
+import javax.persistence.criteria.*;
+import java.lang.reflect.Field;
+import java.util.*;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-6-4 14:59:48
+ */
+@Slf4j
+@SuppressWarnings({"unchecked","all"})
+public class QueryHelp {
+
+    public static <R, Q> Predicate getPredicate(Root<R> root, Q query, CriteriaBuilder cb) {
+        List<Predicate> list = new ArrayList<>();
+        if(query == null){
+            return cb.and(list.toArray(new Predicate[0]));
+        }
+        // 数据权限验证
+        DataPermission permission = query.getClass().getAnnotation(DataPermission.class);
+        if(permission != null){
+            // 获取数据权限
+            List<Long> dataScopes = SecurityUtils.getCurrentUserDataScope();
+            if(CollectionUtil.isNotEmpty(dataScopes)){
+                if(StringUtils.isNotBlank(permission.joinName()) && StringUtils.isNotBlank(permission.fieldName())) {
+                    Join join = root.join(permission.joinName(), JoinType.LEFT);
+                    list.add(getExpression(permission.fieldName(),join, root).in(dataScopes));
+                } else if (StringUtils.isBlank(permission.joinName()) && StringUtils.isNotBlank(permission.fieldName())) {
+                    list.add(getExpression(permission.fieldName(),null, root).in(dataScopes));
+                }
+            }
+        }
+        try {
+            List<Field> fields = getAllFields(query.getClass(), new ArrayList<>());
+            for (Field field : fields) {
+                boolean accessible = field.isAccessible();
+                // 设置对象的访问权限,保证对private的属性的访
+                field.setAccessible(true);
+                Query q = field.getAnnotation(Query.class);
+                if (q != null) {
+                    String propName = q.propName();
+                    String joinName = q.joinName();
+                    String blurry = q.blurry();
+                    String attributeName = isBlank(propName) ? field.getName() : propName;
+                    Class<?> fieldType = field.getType();
+                    Object val = field.get(query);
+                    if (ObjectUtil.isNull(val) || "".equals(val)) {
+                        continue;
+                    }
+                    Join join = null;
+                    // 模糊多字段
+                    if (ObjectUtil.isNotEmpty(blurry)) {
+                        String[] blurrys = blurry.split(",");
+                        List<Predicate> orPredicate = new ArrayList<>();
+                        for (String s : blurrys) {
+                            orPredicate.add(cb.like(root.get(s)
+                                    .as(String.class), "%" + val.toString() + "%"));
+                        }
+                        Predicate[] p = new Predicate[orPredicate.size()];
+                        list.add(cb.or(orPredicate.toArray(p)));
+                        continue;
+                    }
+                    if (ObjectUtil.isNotEmpty(joinName)) {
+                        String[] joinNames = joinName.split(">");
+                        for (String name : joinNames) {
+                            switch (q.join()) {
+                                case LEFT:
+                                    if(ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)){
+                                        join = join.join(name, JoinType.LEFT);
+                                    } else {
+                                        join = root.join(name, JoinType.LEFT);
+                                    }
+                                    break;
+                                case RIGHT:
+                                    if(ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)){
+                                        join = join.join(name, JoinType.RIGHT);
+                                    } else {
+                                        join = root.join(name, JoinType.RIGHT);
+                                    }
+                                    break;
+                                case INNER:
+                                    if(ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)){
+                                        join = join.join(name, JoinType.INNER);
+                                    } else {
+                                        join = root.join(name, JoinType.INNER);
+                                    }
+                                    break;
+                                default: break;
+                            }
+                        }
+                    }
+                    switch (q.type()) {
+                        case EQUAL:
+                            list.add(cb.equal(getExpression(attributeName,join,root)
+                                    .as((Class<? extends Comparable>) fieldType),val));
+                            break;
+                        case GREATER_THAN:
+                            list.add(cb.greaterThanOrEqualTo(getExpression(attributeName,join,root)
+                                    .as((Class<? extends Comparable>) fieldType), (Comparable) val));
+                            break;
+                        case LESS_THAN:
+                            list.add(cb.lessThanOrEqualTo(getExpression(attributeName,join,root)
+                                    .as((Class<? extends Comparable>) fieldType), (Comparable) val));
+                            break;
+                        case LESS_THAN_NQ:
+                            list.add(cb.lessThan(getExpression(attributeName,join,root)
+                                    .as((Class<? extends Comparable>) fieldType), (Comparable) val));
+                            break;
+                        case INNER_LIKE:
+                            list.add(cb.like(getExpression(attributeName,join,root)
+                                    .as(String.class), "%" + val.toString() + "%"));
+                            break;
+                        case LEFT_LIKE:
+                            list.add(cb.like(getExpression(attributeName,join,root)
+                                    .as(String.class), "%" + val.toString()));
+                            break;
+                        case RIGHT_LIKE:
+                            list.add(cb.like(getExpression(attributeName,join,root)
+                                    .as(String.class), val.toString() + "%"));
+                            break;
+                        case IN:
+                            if (CollUtil.isNotEmpty((Collection<Object>)val)) {
+                                list.add(getExpression(attributeName,join,root).in((Collection<Object>) val));
+                            }
+                            break;
+                        case NOT_IN:
+                            if (CollUtil.isNotEmpty((Collection<Object>)val)) {
+                                list.add(getExpression(attributeName,join,root).in((Collection<Object>) val).not());
+                            }
+                            break;
+                        case NOT_EQUAL:
+                            list.add(cb.notEqual(getExpression(attributeName,join,root), val));
+                            break;
+                        case NOT_NULL:
+                            list.add(cb.isNotNull(getExpression(attributeName,join,root)));
+                            break;
+                        case IS_NULL:
+                            list.add(cb.isNull(getExpression(attributeName,join,root)));
+                            break;
+                        case BETWEEN:
+                            List<Object> between = new ArrayList<>((List<Object>)val);
+                            list.add(cb.between(getExpression(attributeName, join, root).as((Class<? extends Comparable>) between.get(0).getClass()),
+                                    (Comparable) between.get(0), (Comparable) between.get(1)));
+                            break;
+                        default: break;
+                    }
+                }
+                field.setAccessible(accessible);
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        int size = list.size();
+        return cb.and(list.toArray(new Predicate[size]));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T, R> Expression<T> getExpression(String attributeName, Join join, Root<R> root) {
+        if (ObjectUtil.isNotEmpty(join)) {
+            return join.get(attributeName);
+        } else {
+            return root.get(attributeName);
+        }
+    }
+
+    private static boolean isBlank(final CharSequence cs) {
+        int strLen;
+        if (cs == null || (strLen = cs.length()) == 0) {
+            return true;
+        }
+        for (int i = 0; i < strLen; i++) {
+            if (!Character.isWhitespace(cs.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static List<Field> getAllFields(Class clazz, List<Field> fields) {
+        if (clazz != null) {
+            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
+            getAllFields(clazz.getSuperclass(), fields);
+        }
+        return fields;
+    }
+}

+ 721 - 0
eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java

@@ -0,0 +1,721 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.*;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author /
+ */
+@Component
+@SuppressWarnings({"unchecked", "all"})
+public class RedisUtils {
+    private static final Logger log = LoggerFactory.getLogger(RedisUtils.class);
+    private RedisTemplate<Object, Object> redisTemplate;
+    @Value("${jwt.online-key}")
+    private String onlineKey;
+
+    public RedisUtils(RedisTemplate<Object, Object> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    /**
+     * 指定缓存失效时间
+     *
+     * @param key  键
+     * @param time 时间(秒)
+     */
+    public boolean expire(String key, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 指定缓存失效时间
+     *
+     * @param key      键
+     * @param time     时间(秒)
+     * @param timeUnit 单位
+     */
+    public boolean expire(String key, long time, TimeUnit timeUnit) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, timeUnit);
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 根据 key 获取过期时间
+     *
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    public long getExpire(Object key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 查找匹配key
+     *
+     * @param pattern key
+     * @return /
+     */
+    public List<String> scan(String pattern) {
+        ScanOptions options = ScanOptions.scanOptions().match(pattern).build();
+        RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
+        RedisConnection rc = Objects.requireNonNull(factory).getConnection();
+        Cursor<byte[]> cursor = rc.scan(options);
+        List<String> result = new ArrayList<>();
+        while (cursor.hasNext()) {
+            result.add(new String(cursor.next()));
+        }
+        try {
+            RedisConnectionUtils.releaseConnection(rc, factory);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return result;
+    }
+
+    /**
+     * 分页查询 key
+     *
+     * @param patternKey key
+     * @param page       页码
+     * @param size       每页数目
+     * @return /
+     */
+    public List<String> findKeysForPage(String patternKey, int page, int size) {
+        ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();
+        RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
+        RedisConnection rc = Objects.requireNonNull(factory).getConnection();
+        Cursor<byte[]> cursor = rc.scan(options);
+        List<String> result = new ArrayList<>(size);
+        int tmpIndex = 0;
+        int fromIndex = page * size;
+        int toIndex = page * size + size;
+        while (cursor.hasNext()) {
+            if (tmpIndex >= fromIndex && tmpIndex < toIndex) {
+                result.add(new String(cursor.next()));
+                tmpIndex++;
+                continue;
+            }
+            // 获取到满足条件的数据后,就可以退出了
+            if (tmpIndex >= toIndex) {
+                break;
+            }
+            tmpIndex++;
+            cursor.next();
+        }
+        try {
+            RedisConnectionUtils.releaseConnection(rc, factory);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return result;
+    }
+
+    /**
+     * 判断key是否存在
+     *
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     *
+     * @param key 可以传一个值 或多个
+     */
+    public void del(String... keys) {
+        if (keys != null && keys.length > 0) {
+            if (keys.length == 1) {
+                boolean result = redisTemplate.delete(keys[0]);
+                log.debug("--------------------------------------------");
+                log.debug(new StringBuilder("删除缓存:").append(keys[0]).append(",结果:").append(result).toString());
+                log.debug("--------------------------------------------");
+            } else {
+                Set<Object> keySet = new HashSet<>();
+                for (String key : keys) {
+                    keySet.addAll(redisTemplate.keys(key));
+                }
+                long count = redisTemplate.delete(keySet);
+                log.debug("--------------------------------------------");
+                log.debug("成功删除缓存:" + keySet.toString());
+                log.debug("缓存删除数量:" + count + "个");
+                log.debug("--------------------------------------------");
+            }
+        }
+    }
+
+    // ============================String=============================
+
+    /**
+     * 普通缓存获取
+     *
+     * @param key 键
+     * @return 值
+     */
+    public Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 批量获取
+     *
+     * @param keys
+     * @return
+     */
+    public List<Object> multiGet(List<String> keys) {
+        List list = redisTemplate.opsForValue().multiGet(Sets.newHashSet(keys));
+        List resultList = Lists.newArrayList();
+        Optional.ofNullable(list).ifPresent(e-> list.forEach(ele-> Optional.ofNullable(ele).ifPresent(resultList::add)));
+        return resultList;
+    }
+
+    /**
+     * 普通缓存放入
+     *
+     * @param key   键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public boolean set(String key, Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public boolean set(String key, Object value, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     *
+     * @param key      键
+     * @param value    值
+     * @param time     时间
+     * @param timeUnit 类型
+     * @return true成功 false 失败
+     */
+    public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, timeUnit);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    // ================================Map=================================
+
+    /**
+     * HashGet
+     *
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     * @return 值
+     */
+    public Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     *
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+
+    }
+
+    /**
+     * HashSet
+     *
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     *
+     * @param key  键
+     * @param map  对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public boolean hmset(String key, Map<String, Object> map, long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value, long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     *
+     * @param key  键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     *
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    public boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要增加几(大于0)
+     * @return
+     */
+    public double hincr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要减少记(小于0)
+     * @return
+     */
+    public double hdecr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+
+    // ============================set=============================
+
+    /**
+     * 根据key获取Set中的所有值
+     *
+     * @param key 键
+     * @return
+     */
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     *
+     * @param key   键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0;
+        }
+    }
+
+    /**
+     * 将set数据放入缓存
+     *
+     * @param key    键
+     * @param time   时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSetAndTime(String key, long time, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return count;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0;
+        }
+    }
+
+    /**
+     * 获取set缓存的长度
+     *
+     * @param key 键
+     * @return
+     */
+    public long sGetSetSize(String key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0;
+        }
+    }
+
+    /**
+     * 移除值为value的
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+     */
+    public long setRemove(String key, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().remove(key, values);
+            return count;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0;
+        }
+    }
+
+    // ===============================list=================================
+
+    /**
+     * 获取list缓存的内容
+     *
+     * @param key   键
+     * @param start 开始
+     * @param end   结束 0 到 -1代表所有值
+     * @return
+     */
+    public List<Object> lGet(String key, long start, long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 获取list缓存的长度
+     *
+     * @param key 键
+     * @return
+     */
+    public long lGetListSize(String key) {
+        try {
+            return redisTemplate.opsForList().size(key);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0;
+        }
+    }
+
+    /**
+     * 通过索引 获取list中的值
+     *
+     * @param key   键
+     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return
+     */
+    public Object lGetIndex(String key, long index) {
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, Object value, long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value, long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     *
+     * @param key   键
+     * @param index 索引
+     * @param value 值
+     * @return /
+     */
+    public boolean lUpdateIndex(String key, long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     *
+     * @param key   键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public long lRemove(String key, long count, Object value) {
+        try {
+            return redisTemplate.opsForList().remove(key, count, value);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0;
+        }
+    }
+
+    /**
+     * @param prefix 前缀
+     * @param ids    id
+     */
+    public void delByKeys(String prefix, Set<String> ids) {
+        Set<Object> keys = new HashSet<>();
+        for (String id : ids) {
+            keys.addAll(redisTemplate.keys(new StringBuffer(prefix).append(id).toString()));
+        }
+        long count = redisTemplate.delete(keys);
+        // 此处提示可自行删除
+        log.debug("--------------------------------------------");
+        log.debug("成功删除缓存:" + keys.toString());
+        log.debug("缓存删除数量:" + count + "个");
+        log.debug("--------------------------------------------");
+    }
+
+    public void delByKey(String prefix, Set<Long> ids) {
+        Set<Object> keys = new HashSet<>();
+        for (Long id : ids) {
+            keys.addAll(redisTemplate.keys(new StringBuffer(prefix).append(id).toString()));
+        }
+        long count = redisTemplate.delete(keys);
+        // 此处提示可自行删除
+        log.debug("--------------------------------------------");
+        log.debug("成功删除缓存:" + keys.toString());
+        log.debug("缓存删除数量:" + count + "个");
+        log.debug("--------------------------------------------");
+    }
+}

+ 33 - 0
eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java

@@ -0,0 +1,33 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Objects;
+
+/**
+ * 获取 HttpServletRequest
+ * @author Zheng Jie
+ * @date 2018-11-24
+ */
+public class RequestHolder {
+
+    public static HttpServletRequest getHttpServletRequest() {
+        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
+    }
+}

+ 198 - 0
eladmin-common/src/main/java/me/zhengjie/utils/RsaUtils.java

@@ -0,0 +1,198 @@
+package me.zhengjie.utils;
+
+import org.apache.commons.codec.binary.Base64;
+import javax.crypto.Cipher;
+import java.io.ByteArrayOutputStream;
+import java.security.*;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * @author https://www.cnblogs.com/nihaorz/p/10690643.html
+ * @description Rsa 工具类,公钥私钥生成,加解密
+ * @date 2020-05-18
+ **/
+public class RsaUtils {
+
+    private static final String SRC = "123456";
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("\n");
+        RsaKeyPair keyPair = generateKeyPair();
+        System.out.println("公钥:" + keyPair.getPublicKey());
+        System.out.println("私钥:" + keyPair.getPrivateKey());
+        System.out.println("\n");
+        test1(keyPair);
+        System.out.println("\n");
+        test2(keyPair);
+        System.out.println("\n");
+    }
+
+    /**
+     * 公钥加密私钥解密
+     */
+    private static void test1(RsaKeyPair keyPair) throws Exception {
+        System.out.println("***************** 公钥加密私钥解密开始 *****************");
+        String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC);
+        String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);
+        System.out.println("加密前:" + RsaUtils.SRC);
+        System.out.println("加密后:" + text1);
+        System.out.println("解密后:" + text2);
+        if (RsaUtils.SRC.equals(text2)) {
+            System.out.println("解密字符串和原始字符串一致,解密成功");
+        } else {
+            System.out.println("解密字符串和原始字符串不一致,解密失败");
+        }
+        System.out.println("***************** 公钥加密私钥解密结束 *****************");
+    }
+
+    /**
+     * 私钥加密公钥解密
+     * @throws Exception /
+     */
+    private static void test2(RsaKeyPair keyPair) throws Exception {
+        System.out.println("***************** 私钥加密公钥解密开始 *****************");
+        String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), RsaUtils.SRC);
+        String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1);
+        System.out.println("加密前:" + RsaUtils.SRC);
+        System.out.println("加密后:" + text1);
+        System.out.println("解密后:" + text2);
+        if (RsaUtils.SRC.equals(text2)) {
+            System.out.println("解密字符串和原始字符串一致,解密成功");
+        } else {
+            System.out.println("解密字符串和原始字符串不一致,解密失败");
+        }
+        System.out.println("***************** 私钥加密公钥解密结束 *****************");
+    }
+
+    /**
+     * 公钥解密
+     *
+     * @param publicKeyText 公钥
+     * @param text 待解密的信息
+     * @return /
+     * @throws Exception /
+     */
+    public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
+        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.DECRYPT_MODE, publicKey);
+        byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text));
+        return new String(result);
+    }
+
+    /**
+     * 私钥加密
+     *
+     * @param privateKeyText 私钥
+     * @param text 待加密的信息
+     * @return /
+     * @throws Exception /
+     */
+    public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
+        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
+        byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes());
+        return Base64.encodeBase64String(result);
+    }
+
+    /**
+     * 私钥解密
+     *
+     * @param privateKeyText 私钥
+     * @param text 待解密的文本
+     * @return /
+     * @throws Exception /
+     */
+    public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
+        PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.DECRYPT_MODE, privateKey);
+        byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text));
+        return new String(result);
+    }
+
+    /**
+     * 公钥加密
+     *
+     * @param publicKeyText 公钥
+     * @param text 待加密的文本
+     * @return /
+     */
+    public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
+        X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+        byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes());
+        return Base64.encodeBase64String(result);
+    }
+
+    private static byte[] doLongerCipherFinal(int opMode,Cipher cipher, byte[] source) throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        if (opMode == Cipher.DECRYPT_MODE) {
+            out.write(cipher.doFinal(source));
+        } else {
+            int offset = 0;
+            int totalSize = source.length;
+            while (totalSize - offset > 0) {
+                int size = Math.min(cipher.getOutputSize(0) - 11, totalSize - offset);
+                out.write(cipher.doFinal(source, offset, size));
+                offset += size;
+            }
+        }
+        out.close();
+        return out.toByteArray();
+    }
+
+    /**
+     * 构建RSA密钥对
+     *
+     * @return /
+     * @throws NoSuchAlgorithmException /
+     */
+    public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {
+        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+        keyPairGenerator.initialize(1024);
+        KeyPair keyPair = keyPairGenerator.generateKeyPair();
+        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
+        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
+        String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
+        String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
+        return new RsaKeyPair(publicKeyString, privateKeyString);
+    }
+
+
+    /**
+     * RSA密钥对对象
+     */
+    public static class RsaKeyPair {
+
+        private final String publicKey;
+        private final String privateKey;
+
+        public RsaKeyPair(String publicKey, String privateKey) {
+            this.publicKey = publicKey;
+            this.privateKey = privateKey;
+        }
+
+        public String getPublicKey() {
+            return publicKey;
+        }
+
+        public String getPrivateKey() {
+            return privateKey;
+        }
+
+    }
+}

+ 161 - 0
eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java

@@ -0,0 +1,161 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+import me.zhengjie.base.QueryPageParams;
+import me.zhengjie.exception.BadRequestException;
+import me.zhengjie.utils.enums.DataScopeEnum;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * 获取当前登录的用户
+ * @author Zheng Jie
+ * @date 2019-01-17
+ */
+@Slf4j
+public class SecurityUtils {
+
+    /**
+     * 获取当前登录的用户
+     * @return UserDetails
+     */
+    public static UserDetails getCurrentUser() {
+        UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class);
+        return userDetailsService.loadUserByUsername(getCurrentUsername());
+    }
+
+    /**
+     * 获取系统用户名称
+     *
+     * @return 系统用户名称
+     */
+    public static String getCurrentUsername() {
+        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication == null) {
+            throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期");
+        }
+        if (authentication.getPrincipal() instanceof UserDetails) {
+            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
+            return userDetails.getUsername();
+        }
+        throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息");
+    }
+
+    /**
+     * 获取系统用户ID
+     * @return 系统用户ID
+     */
+    public static String getCurrentUserId() {
+        UserDetails userDetails = getCurrentUser();
+        return new JSONObject(new JSONObject(userDetails).get("user")).get("id", String.class);
+    }
+
+    /**
+     * 获取当前用户的数据权限
+     * @return /
+     */
+    public static List<Long> getCurrentUserDataScope(){
+        UserDetails userDetails = getCurrentUser();
+        JSONArray array = JSONUtil.parseArray(new JSONObject(userDetails).get("dataScopes"));
+        return JSONUtil.toList(array,Long.class);
+    }
+
+    /**
+     * 获取数据权限级别
+     * @return 级别
+     */
+    public static String getDataScopeType() {
+        List<Long> dataScopes = getCurrentUserDataScope();
+        if(dataScopes.size() != 0){
+            return "";
+        }
+        return DataScopeEnum.ALL.getValue();
+    }
+
+    /**
+     * 验证API访问权限
+     */
+    public static void CheckApiAuth(QueryPageParams params) {
+        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        if (request != null) {
+            String accesstoken = request.getHeader("XYTACCESSTOKEN");
+            String token = "DMERPYT!@#$QWER2021+{:>";
+
+            if (params == null || StringUtils.isBlank(params.getTimestamp()) || StringUtils.isBlank(params.getNonce()) || StringUtils.isBlank(accesstoken)) {
+                throw new BadRequestException(HttpStatus.UNAUTHORIZED, "认证失败");
+            }
+
+            String[] arr = {token, params.getTimestamp(), params.getNonce()};
+            Arrays.sort(arr);
+
+            String str = "";
+            for (int i = 0; i < arr.length; i++) {
+                str += arr[i];
+            }
+            String newtoken = EncryptUtils.sha1(str);
+            if (!accesstoken.equals(newtoken)) {
+                throw new BadRequestException(HttpStatus.UNAUTHORIZED, "认证失败");
+            }
+        }
+    }
+
+    /**
+     * 根据key获取token
+     */
+    public static Map<String,Object> getToken(String key){
+        //获取11位随机数
+        double rand = Math.random();
+        String randStr = String.valueOf(rand).replace("0.", "");
+        String nonce = randStr.substring(0, 11);
+        System.out.println("nonce:"+nonce);
+        //获取当前时间
+        SimpleDateFormat sdf = new SimpleDateFormat();
+        sdf.applyPattern("yyyy-MM-dd HH:mm");
+        Date date = new Date();
+        String timestamp =sdf.format(date);
+        System.out.println("timestamp:"+timestamp);
+        //排序
+        String[] arr = {key, timestamp, nonce};
+        Arrays.sort(arr);
+        //获取token
+        String str = "";
+        for (int i = 0; i < arr.length; i++) {
+            str += arr[i];
+        }
+        String token = EncryptUtils.sha1(str);
+        System.out.println("token:"+token);
+        Map<String,Object> data = new HashMap<String, Object>(1) {{
+            put("nonce", nonce);
+            put("timestamp", timestamp);
+            put("token", token);
+        }};
+        return data;
+    }
+}

+ 145 - 0
eladmin-common/src/main/java/me/zhengjie/utils/SpringContextHolder.java

@@ -0,0 +1,145 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.env.Environment;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Jie
+ * @date 2019-01-07
+ */
+@Slf4j
+public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
+
+    private static ApplicationContext applicationContext = null;
+    private static final List<CallBack> CALL_BACKS = new ArrayList<>();
+    private static boolean addCallback = true;
+
+    /**
+     * 针对 某些初始化方法,在SpringContextHolder 未初始化时 提交回调方法。
+     * 在SpringContextHolder 初始化后,进行回调使用
+     *
+     * @param callBack 回调函数
+     */
+    public synchronized static void addCallBacks(CallBack callBack) {
+        if (addCallback) {
+            SpringContextHolder.CALL_BACKS.add(callBack);
+        } else {
+            log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName());
+            callBack.executor();
+        }
+    }
+
+    /**
+     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) {
+        assertContextInjected();
+        return (T) applicationContext.getBean(name);
+    }
+
+    /**
+     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+     */
+    public static <T> T getBean(Class<T> requiredType) {
+        assertContextInjected();
+        return applicationContext.getBean(requiredType);
+    }
+
+    /**
+     * 获取SpringBoot 配置信息
+     *
+     * @param property     属性key
+     * @param defaultValue 默认值
+     * @param requiredType 返回类型
+     * @return /
+     */
+    public static <T> T getProperties(String property, T defaultValue, Class<T> requiredType) {
+        T result = defaultValue;
+        try {
+            result = getBean(Environment.class).getProperty(property, requiredType);
+        } catch (Exception ignored) {}
+        return result;
+    }
+
+    /**
+     * 获取SpringBoot 配置信息
+     *
+     * @param property 属性key
+     * @return /
+     */
+    public static String getProperties(String property) {
+        return getProperties(property, null, String.class);
+    }
+
+    /**
+     * 获取SpringBoot 配置信息
+     *
+     * @param property     属性key
+     * @param requiredType 返回类型
+     * @return /
+     */
+    public static <T> T getProperties(String property, Class<T> requiredType) {
+        return getProperties(property, null, requiredType);
+    }
+
+    /**
+     * 检查ApplicationContext不为空.
+     */
+    private static void assertContextInjected() {
+        if (applicationContext == null) {
+            throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
+                    ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
+        }
+    }
+
+    /**
+     * 清除SpringContextHolder中的ApplicationContext为Null.
+     */
+    private static void clearHolder() {
+        log.debug("清除SpringContextHolder中的ApplicationContext:"
+                + applicationContext);
+        applicationContext = null;
+    }
+
+    @Override
+    public void destroy() {
+        SpringContextHolder.clearHolder();
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        if (SpringContextHolder.applicationContext != null) {
+            log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
+        }
+        SpringContextHolder.applicationContext = applicationContext;
+        if (addCallback) {
+            for (CallBack callBack : SpringContextHolder.CALL_BACKS) {
+                callBack.executor();
+            }
+            CALL_BACKS.clear();
+        }
+        SpringContextHolder.addCallback = false;
+    }
+}

+ 294 - 0
eladmin-common/src/main/java/me/zhengjie/utils/StringUtils.java

@@ -0,0 +1,294 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import nl.basjes.parse.useragent.UserAgent;
+import nl.basjes.parse.useragent.UserAgentAnalyzer;
+import org.lionsoul.ip2region.DataBlock;
+import org.lionsoul.ip2region.DbConfig;
+import org.lionsoul.ip2region.DbSearcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.ClassPathResource;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.UnknownHostException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+
+/**
+ * @author Zheng Jie
+ * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils {
+
+    private static final Logger log = LoggerFactory.getLogger(StringUtils.class);
+    private static boolean ipLocal = false;
+    private static File file = null;
+    private static DbConfig config;
+    private static final char SEPARATOR = '_';
+    private static final String UNKNOWN = "unknown";
+
+    private static final UserAgentAnalyzer userAgentAnalyzer = UserAgentAnalyzer
+            .newBuilder()
+            .hideMatcherLoadStats()
+            .withCache(10000)
+            .withField(UserAgent.AGENT_NAME_VERSION)
+            .build();
+
+
+    static {
+        SpringContextHolder.addCallBacks(() -> {
+            StringUtils.ipLocal = SpringContextHolder.getProperties("ip.local-parsing", false, Boolean.class);
+            if (ipLocal) {
+                /*
+                 * 此文件为独享 ,不必关闭
+                 */
+                String path = "ip2region/ip2region.db";
+                String name = "ip2region.db";
+                try {
+                    config = new DbConfig();
+                    file = FileUtil.inputStreamToFile(new ClassPathResource(path).getInputStream(), name);
+                } catch (Exception e) {
+                    log.error(e.getMessage(), e);
+                }
+            }
+        });
+    }
+
+    /**
+     * 驼峰命名法工具
+     *
+     * @return toCamelCase(" hello_world ") == "helloWorld"
+     * toCapitalizeCamelCase("hello_world") == "HelloWorld"
+     * toUnderScoreCase("helloWorld") = "hello_world"
+     */
+    public static String toCamelCase(String s) {
+        if (s == null) {
+            return null;
+        }
+
+        s = s.toLowerCase();
+
+        StringBuilder sb = new StringBuilder(s.length());
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+
+            if (c == SEPARATOR) {
+                upperCase = true;
+            } else if (upperCase) {
+                sb.append(Character.toUpperCase(c));
+                upperCase = false;
+            } else {
+                sb.append(c);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 驼峰命名法工具
+     *
+     * @return toCamelCase(" hello_world ") == "helloWorld"
+     * toCapitalizeCamelCase("hello_world") == "HelloWorld"
+     * toUnderScoreCase("helloWorld") = "hello_world"
+     */
+    public static String toCapitalizeCamelCase(String s) {
+        if (s == null) {
+            return null;
+        }
+        s = toCamelCase(s);
+        return s.substring(0, 1).toUpperCase() + s.substring(1);
+    }
+
+    /**
+     * 驼峰命名法工具
+     *
+     * @return toCamelCase(" hello_world ") == "helloWorld"
+     * toCapitalizeCamelCase("hello_world") == "HelloWorld"
+     * toUnderScoreCase("helloWorld") = "hello_world"
+     */
+    static String toUnderScoreCase(String s) {
+        if (s == null) {
+            return null;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+
+            boolean nextUpperCase = true;
+
+            if (i < (s.length() - 1)) {
+                nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
+            }
+
+            if ((i > 0) && Character.isUpperCase(c)) {
+                if (!upperCase || !nextUpperCase) {
+                    sb.append(SEPARATOR);
+                }
+                upperCase = true;
+            } else {
+                upperCase = false;
+            }
+
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 获取ip地址
+     */
+    public static String getIp(HttpServletRequest request) {
+        String ip = request.getHeader("x-forwarded-for");
+        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        String comma = ",";
+        String localhost = "127.0.0.1";
+        if (ip.contains(comma)) {
+            ip = ip.split(",")[0];
+        }
+        if (localhost.equals(ip)) {
+            // 获取本机真正的ip地址
+            try {
+                ip = InetAddress.getLocalHost().getHostAddress();
+            } catch (UnknownHostException e) {
+                log.error(e.getMessage(), e);
+            }
+        }
+        return ip;
+    }
+
+    /**
+     * 根据ip获取详细地址
+     */
+    public static String getCityInfo(String ip) {
+        if (ipLocal) {
+            return getLocalCityInfo(ip);
+        } else {
+            return getHttpCityInfo(ip);
+        }
+    }
+
+    /**
+     * 根据ip获取详细地址
+     */
+    public static String getHttpCityInfo(String ip) {
+        String api = String.format(ElAdminConstant.Url.IP_URL, ip);
+        JSONObject object = JSONUtil.parseObj(HttpUtil.get(api));
+        return object.get("addr", String.class);
+    }
+
+    /**
+     * 根据ip获取详细地址
+     */
+    public static String getLocalCityInfo(String ip) {
+        try {
+            DataBlock dataBlock = new DbSearcher(config, file.getPath())
+                    .binarySearch(ip);
+            String region = dataBlock.getRegion();
+            String address = region.replace("0|", "");
+            char symbol = '|';
+            if (address.charAt(address.length() - 1) == symbol) {
+                address = address.substring(0, address.length() - 1);
+            }
+            return address.equals(ElAdminConstant.REGION) ? "内网IP" : address;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return "";
+    }
+
+    public static String getBrowser(HttpServletRequest request) {
+        UserAgent.ImmutableUserAgent userAgent = userAgentAnalyzer.parse(request.getHeader("User-Agent"));
+        return userAgent.get(UserAgent.AGENT_NAME_VERSION).getValue();
+    }
+
+    /**
+     * 获得当天是周几
+     */
+    public static String getWeekDay() {
+        String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(new Date());
+
+        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
+        if (w < 0) {
+            w = 0;
+        }
+        return weekDays[w];
+    }
+
+    /**
+     * 获取当前机器的IP
+     *
+     * @return /
+     */
+    public static String getLocalIp() {
+        try {
+            InetAddress candidateAddress = null;
+            // 遍历所有的网络接口
+            for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements();) {
+                NetworkInterface anInterface = interfaces.nextElement();
+                // 在所有的接口下再遍历IP
+                for (Enumeration<InetAddress> inetAddresses = anInterface.getInetAddresses(); inetAddresses.hasMoreElements();) {
+                    InetAddress inetAddr = inetAddresses.nextElement();
+                    // 排除loopback类型地址
+                    if (!inetAddr.isLoopbackAddress()) {
+                        if (inetAddr.isSiteLocalAddress()) {
+                            // 如果是site-local地址,就是它了
+                            return inetAddr.getHostAddress();
+                        } else if (candidateAddress == null) {
+                            // site-local类型的地址未被发现,先记录候选地址
+                            candidateAddress = inetAddr;
+                        }
+                    }
+                }
+            }
+            if (candidateAddress != null) {
+                return candidateAddress.getHostAddress();
+            }
+            // 如果没有发现 non-loopback地址.只能用最次选的方案
+            InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
+            if (jdkSuppliedAddress == null) {
+                return "";
+            }
+            return jdkSuppliedAddress.getHostAddress();
+        } catch (Exception e) {
+            return "";
+        }
+    }
+}

+ 37 - 0
eladmin-common/src/main/java/me/zhengjie/utils/ThrowableUtil.java

@@ -0,0 +1,37 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * 异常工具 2019-01-06
+ * @author Zheng Jie
+ */
+public class ThrowableUtil {
+
+    /**
+     * 获取堆栈信息
+     */
+    public static String getStackTrace(Throwable throwable){
+        StringWriter sw = new StringWriter();
+        try (PrintWriter pw = new PrintWriter(sw)) {
+            throwable.printStackTrace(pw);
+            return sw.toString();
+        }
+    }
+}

+ 66 - 0
eladmin-common/src/main/java/me/zhengjie/utils/TranslatorUtil.java

@@ -0,0 +1,66 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import cn.hutool.json.JSONArray;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+
+/**
+ * @author Zheng Jie
+ * 翻译工具类
+ */
+public class TranslatorUtil {
+
+    public static String translate(String word){
+        try {
+            String url = "https://translate.googleapis.com/translate_a/single?" +
+                    "client=gtx&" +
+                    "sl=en" +
+                    "&tl=zh-CN" +
+                    "&dt=t&q=" + URLEncoder.encode(word, "UTF-8");
+
+            URL obj = new URL(url);
+            HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+            con.setRequestProperty("User-Agent", "Mozilla/5.0");
+
+            BufferedReader in = new BufferedReader(
+                    new InputStreamReader(con.getInputStream()));
+            String inputLine;
+            StringBuilder response = new StringBuilder();
+
+            while ((inputLine = in.readLine()) != null) {
+                response.append(inputLine);
+            }
+            in.close();
+            return parseResult(response.toString());
+        }catch (Exception e){
+          return  word;
+        }
+    }
+
+    private static String parseResult(String inputJson){
+        JSONArray jsonArray2 = (JSONArray) new JSONArray(inputJson).get(0);
+        StringBuilder result = new StringBuilder();
+        for (Object o : jsonArray2) {
+            result.append(((JSONArray) o).get(0).toString());
+        }
+        return result.toString();
+    }
+}

+ 45 - 0
eladmin-common/src/main/java/me/zhengjie/utils/ValidationUtil.java

@@ -0,0 +1,45 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import cn.hutool.core.util.ObjectUtil;
+import me.zhengjie.exception.BadRequestException;
+import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
+
+/**
+ * 验证工具
+ * @author Zheng Jie
+ * @date 2018-11-23
+ */
+public class ValidationUtil{
+
+    /**
+     * 验证空
+     */
+    public static void isNull(Object obj, String entity, String parameter , Object value){
+        if(ObjectUtil.isNull(obj)){
+            String msg = entity + " 不存在: "+ parameter +" is "+ value;
+            throw new BadRequestException(msg);
+        }
+    }
+
+    /**
+     * 验证是否为邮箱
+     */
+    public static boolean isEmail(String email) {
+        return new EmailValidator().isValid(email, null);
+    }
+}

+ 111 - 0
eladmin-common/src/main/java/me/zhengjie/utils/WxUtil.java

@@ -0,0 +1,111 @@
+package me.zhengjie.utils;
+import cn.hutool.http.HttpRequest;
+import com.alibaba.fastjson.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.concurrent.TimeUnit;
+
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class WxUtil {
+
+    private final RedisUtils redisUtils;
+
+    /** TokenURL */
+    private final static String GET_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
+
+    /** TicketUrl */
+    private final static String GET_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
+
+    /** APP_ID */
+    private final static String APP_ID = "wxd64360a4b8c50006";
+
+    /** APP_SECRET */
+    private final static String APP_SECRET = "b758e45c89162542610509dafd9db7c3";
+
+
+    /**
+     * 获取token,默认7200秒过期,所以存redis7200秒取一次
+     *
+     * @return token
+     */
+    public Object getToken() {
+        String redisKey = "Tencent:AccessToken";
+        Object token  = redisUtils.get(redisKey);
+        if(ObjectUtils.isEmpty(token)){
+            token = token();
+            //设置缓存 2小时
+            redisUtils.set(redisKey,token, 7200, TimeUnit.SECONDS);
+            log.info("result:redisToken {}",redisUtils.get(redisKey));
+        }
+        return token;
+    }
+
+
+    public String token(){
+        String result = HttpRequest.get(GET_TOKEN_URL+"&appid="+APP_ID+"&secret="+APP_SECRET).execute().body();
+        log.info("result:token "+result);
+        if (!StringUtils.isNotEmpty(result)) {
+            return null;
+        }
+        JSONObject object = JSONObject.parseObject(result);
+        return ObjectUtils.isEmpty(object) ? null : object.getString("access_token");
+    }
+
+    //getResponseJson();
+    private JSONObject getResponseJson(HttpResponse response) throws IOException {
+        JSONObject json = null;
+        HttpEntity entity = response.getEntity();
+        if(entity!=null){
+            String result = EntityUtils.toString(entity,"UTF-8");
+            json = JSONObject.parseObject(result);
+        }
+        if(ObjectUtils.isEmpty(json)){
+            return null;
+        }
+        return json;
+    }
+
+    /**
+     * 获取ticket,默认7200秒过期,所以存redis7200秒取一次
+     *
+     * @return ticket
+     */
+    public Object getTicket() {
+        String ticketKey = "Tencent:JsapiTicket";
+        Object ticket  = redisUtils.get(ticketKey);
+        if(ObjectUtils.isEmpty(ticket)){
+            ticket = ticket();
+            //设置缓存 2小时
+            redisUtils.set(ticketKey,ticket, 7200, TimeUnit.SECONDS);
+        }
+        return ticket;
+    }
+
+
+    public String ticket(){
+        String result = HttpRequest.get(GET_TICKET_URL+"&access_token="+getToken().toString()).execute().body();
+        log.info("result:ticket"+result);
+        if (!StringUtils.isNotEmpty(result)) {
+            return null;
+        }
+        JSONObject object = JSONObject.parseObject(result);
+        return ObjectUtils.isEmpty(object) ? null : object.getString("ticket");
+    }
+
+}

+ 50 - 0
eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeBiEnum.java

@@ -0,0 +1,50 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * <p>
+ * 验证码业务场景
+ * </p>
+ * @author Zheng Jie
+ * @date 2020-05-02
+ */
+@Getter
+@AllArgsConstructor
+public enum CodeBiEnum {
+
+    /* 旧邮箱修改邮箱 */
+    ONE(1, "旧邮箱修改邮箱"),
+
+    /* 通过邮箱修改密码 */
+    TWO(2, "通过邮箱修改密码");
+
+    private final Integer code;
+    private final String description;
+
+    public static CodeBiEnum find(Integer code) {
+        for (CodeBiEnum value : CodeBiEnum.values()) {
+            if (code.equals(value.getCode())) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+}

+ 46 - 0
eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeEnum.java

@@ -0,0 +1,46 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * <p>
+ * 验证码业务场景对应的 Redis 中的 key
+ * </p>
+ * @author Zheng Jie
+ * @date 2020-05-02
+ */
+@Getter
+@AllArgsConstructor
+public enum CodeEnum {
+
+    /* 通过手机号码重置邮箱 */
+    PHONE_RESET_EMAIL_CODE("phone_reset_email_code_", "通过手机号码重置邮箱"),
+
+    /* 通过旧邮箱重置邮箱 */
+    EMAIL_RESET_EMAIL_CODE("email_reset_email_code_", "通过旧邮箱重置邮箱"),
+
+    /* 通过手机号码重置密码 */
+    PHONE_RESET_PWD_CODE("phone_reset_pwd_code_", "通过手机号码重置密码"),
+
+    /* 通过邮箱重置密码 */
+    EMAIL_RESET_PWD_CODE("email_reset_pwd_code_", "通过邮箱重置密码");
+
+    private final String key;
+    private final String description;
+}

+ 53 - 0
eladmin-common/src/main/java/me/zhengjie/utils/enums/DataScopeEnum.java

@@ -0,0 +1,53 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * <p>
+ * 数据权限枚举
+ * </p>
+ * @author Zheng Jie
+ * @date 2020-05-07
+ */
+@Getter
+@AllArgsConstructor
+public enum DataScopeEnum {
+
+    /* 全部的数据权限 */
+    ALL("全部", "全部的数据权限"),
+
+    /* 自己部门的数据权限 */
+    THIS_LEVEL("本级", "自己部门的数据权限"),
+
+    /* 自定义的数据权限 */
+    CUSTOMIZE("自定义", "自定义的数据权限");
+
+    private final String value;
+    private final String description;
+
+    public static DataScopeEnum find(String val) {
+        for (DataScopeEnum dataScopeEnum : DataScopeEnum.values()) {
+            if (val.equals(dataScopeEnum.getValue())) {
+                return dataScopeEnum;
+            }
+        }
+        return null;
+    }
+
+}

+ 74 - 0
eladmin-common/src/main/java/me/zhengjie/utils/enums/RequestMethodEnum.java

@@ -0,0 +1,74 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author Zheng Jie
+ * @website https://el-admin.vip
+ * @description
+ * @date 2020-06-10
+ **/
+@Getter
+@AllArgsConstructor
+public enum RequestMethodEnum {
+
+    /**
+     * 搜寻 @AnonymousGetMapping
+     */
+    GET("GET"),
+
+    /**
+     * 搜寻 @AnonymousPostMapping
+     */
+    POST("POST"),
+
+    /**
+     * 搜寻 @AnonymousPutMapping
+     */
+    PUT("PUT"),
+
+    /**
+     * 搜寻 @AnonymousPatchMapping
+     */
+    PATCH("PATCH"),
+
+    /**
+     * 搜寻 @AnonymousDeleteMapping
+     */
+    DELETE("DELETE"),
+
+    /**
+     * 否则就是所有 Request 接口都放行
+     */
+    ALL("All");
+
+    /**
+     * Request 类型
+     */
+    private final String type;
+
+    public static RequestMethodEnum find(String type) {
+        for (RequestMethodEnum value : RequestMethodEnum.values()) {
+            if (type.equals(value.getType())) {
+                return value;
+            }
+        }
+        return ALL;
+    }
+}

+ 26 - 0
eladmin-common/src/test/java/me/zhengjie/utils/DateUtilsTest.java

@@ -0,0 +1,26 @@
+package me.zhengjie.utils;
+
+import org.junit.Test;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+public class DateUtilsTest {
+    @Test
+    public void test1() {
+        long l = System.currentTimeMillis() / 1000;
+        LocalDateTime localDateTime = DateUtil.fromTimeStamp(l);
+        System.out.print(DateUtil.localDateTimeFormatyMdHms(localDateTime));
+    }
+
+    @Test
+    public void test2() {
+        LocalDateTime now = LocalDateTime.now();
+        System.out.println(DateUtil.localDateTimeFormatyMdHms(now));
+        Date date = DateUtil.toDate(now);
+        LocalDateTime localDateTime = DateUtil.toLocalDateTime(date);
+        System.out.println(DateUtil.localDateTimeFormatyMdHms(localDateTime));
+        LocalDateTime localDateTime1 = DateUtil.fromTimeStamp(date.getTime() / 1000);
+        System.out.println(DateUtil.localDateTimeFormatyMdHms(localDateTime1));
+    }
+}

+ 32 - 0
eladmin-common/src/test/java/me/zhengjie/utils/EncryptUtilsTest.java

@@ -0,0 +1,32 @@
+package me.zhengjie.utils;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static me.zhengjie.utils.EncryptUtils.*;
+
+public class EncryptUtilsTest {
+
+    /**
+     * 对称加密
+     */
+    @Test
+    public void testDesEncrypt() {
+        try {
+            assertEquals("7772841DC6099402", desEncrypt("123456"));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 对称解密
+     */
+    @Test
+    public void testDesDecrypt() {
+        try {
+            assertEquals("123456", desDecrypt("7772841DC6099402"));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 36 - 0
eladmin-common/src/test/java/me/zhengjie/utils/FileUtilTest.java

@@ -0,0 +1,36 @@
+package me.zhengjie.utils;
+
+import org.junit.Test;
+import org.springframework.mock.web.MockMultipartFile;
+
+import static org.junit.Assert.*;
+import static me.zhengjie.utils.FileUtil.*;
+
+public class FileUtilTest {
+
+    @Test
+    public void testToFile() {
+        long retval = toFile(new MockMultipartFile("foo", (byte[]) null)).getTotalSpace();
+        assertEquals(500695072768L, retval);
+    }
+
+    @Test
+    public void testGetExtensionName() {
+        assertEquals("foo", getExtensionName("foo"));
+        assertEquals("exe", getExtensionName("bar.exe"));
+    }
+
+    @Test
+    public void testGetFileNameNoEx() {
+        assertEquals("foo", getFileNameNoEx("foo"));
+        assertEquals("bar", getFileNameNoEx("bar.txt"));
+    }
+
+    @Test
+    public void testGetSize() {
+        assertEquals("1000B   ", getSize(1000));
+        assertEquals("1.00KB   ", getSize(1024));
+        assertEquals("1.00MB   ", getSize(1048576));
+        assertEquals("1.00GB   ", getSize(1073741824));
+    }
+}

+ 43 - 0
eladmin-common/src/test/java/me/zhengjie/utils/StringUtilsTest.java

@@ -0,0 +1,43 @@
+package me.zhengjie.utils;
+
+import org.junit.Test;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import static me.zhengjie.utils.StringUtils.*;
+import static org.junit.Assert.*;
+
+public class StringUtilsTest {
+
+    @Test
+    public void testToCamelCase() {
+        assertNull(toCamelCase(null));
+    }
+
+    @Test
+    public void testToCapitalizeCamelCase() {
+        assertNull(StringUtils.toCapitalizeCamelCase(null));
+        assertEquals("HelloWorld", toCapitalizeCamelCase("hello_world"));
+    }
+
+    @Test
+    public void testToUnderScoreCase() {
+        assertNull(StringUtils.toUnderScoreCase(null));
+        assertEquals("hello_world", toUnderScoreCase("helloWorld"));
+        assertEquals("\u0000\u0000", toUnderScoreCase("\u0000\u0000"));
+        assertEquals("\u0000_a", toUnderScoreCase("\u0000A"));
+    }
+
+    @Test
+    public void testGetWeekDay() {
+        SimpleDateFormat simpleDateformat = new SimpleDateFormat("E");
+        assertEquals(simpleDateformat.format(new Date()), getWeekDay());
+    }
+
+    @Test
+    public void testGetIP() {
+        assertEquals("127.0.0.1", getIp(new MockHttpServletRequest()));
+    }
+}

+ 39 - 0
eladmin-generator/pom.xml

@@ -0,0 +1,39 @@
+<?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>eladmin</artifactId>
+        <groupId>me.zhengjie</groupId>
+        <version>2.6</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>eladmin-generator</artifactId>
+    <name>代码生成模块</name>
+
+    <properties>
+        <configuration.version>1.9</configuration.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>me.zhengjie</groupId>
+            <artifactId>eladmin-common</artifactId>
+            <version>2.6</version>
+        </dependency>
+
+        <!--模板引擎-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-freemarker</artifactId>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/commons-configuration/commons-configuration -->
+        <dependency>
+            <groupId>commons-configuration</groupId>
+            <artifactId>commons-configuration</artifactId>
+            <version>${configuration.version}</version>
+        </dependency>
+    </dependencies>
+</project>

+ 97 - 0
eladmin-generator/src/main/java/me/zhengjie/domain/ColumnInfo.java

@@ -0,0 +1,97 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.zhengjie.utils.GenUtil;
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * 列的数据信息
+ * @author Zheng Jie
+ * @date 2019-01-02
+ */
+@Getter
+@Setter
+@Entity
+@NoArgsConstructor
+@Table(name = "code_column_config")
+public class ColumnInfo implements Serializable {
+
+    @Id
+    @Column(name = "column_id")
+    @ApiModelProperty(value = "ID", hidden = true)
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ApiModelProperty(value = "表名")
+    private String tableName;
+
+    @ApiModelProperty(value = "数据库字段名称")
+    private String columnName;
+
+    @ApiModelProperty(value = "数据库字段类型")
+    private String columnType;
+
+    @ApiModelProperty(value = "数据库字段键类型")
+    private String keyType;
+
+    @ApiModelProperty(value = "字段额外的参数")
+    private String extra;
+
+    @ApiModelProperty(value = "数据库字段描述")
+    private String remark;
+
+    @ApiModelProperty(value = "是否必填")
+    private Boolean notNull;
+
+    @ApiModelProperty(value = "是否在列表显示")
+    private Boolean listShow;
+
+    @ApiModelProperty(value = "是否表单显示")
+    private Boolean formShow;
+
+    @ApiModelProperty(value = "表单类型")
+    private String formType;
+
+    @ApiModelProperty(value = "查询 1:模糊 2:精确")
+    private String queryType;
+
+    @ApiModelProperty(value = "字典名称")
+    private String dictName;
+
+    @ApiModelProperty(value = "日期注解")
+    private String dateAnnotation;
+
+    public ColumnInfo(String tableName, String columnName, Boolean notNull, String columnType, String remark, String keyType, String extra) {
+        this.tableName = tableName;
+        this.columnName = columnName;
+        this.columnType = columnType;
+        this.keyType = keyType;
+        this.extra = extra;
+        this.notNull = notNull;
+        if(GenUtil.PK.equalsIgnoreCase(keyType) && GenUtil.EXTRA.equalsIgnoreCase(extra)){
+            this.notNull = false;
+        }
+        this.remark = remark;
+        this.listShow = true;
+        this.formShow = true;
+    }
+}

+ 78 - 0
eladmin-generator/src/main/java/me/zhengjie/domain/GenConfig.java

@@ -0,0 +1,78 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import javax.persistence.*;
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * 代码生成配置
+ * @author Zheng Jie
+ * @date 2019-01-03
+ */
+@Getter
+@Setter
+@Entity
+@NoArgsConstructor
+@Table(name = "code_gen_config")
+public class GenConfig implements Serializable {
+
+    public GenConfig(String tableName) {
+        this.tableName = tableName;
+    }
+
+    @Id
+    @Column(name = "config_id")
+    @ApiModelProperty(value = "ID", hidden = true)
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @NotBlank
+    @ApiModelProperty(value = "表名")
+    private String tableName;
+
+    @ApiModelProperty(value = "接口名称")
+    private String apiAlias;
+
+    @NotBlank
+    @ApiModelProperty(value = "包路径")
+    private String pack;
+
+    @NotBlank
+    @ApiModelProperty(value = "模块名")
+    private String moduleName;
+
+    @NotBlank
+    @ApiModelProperty(value = "前端文件路径")
+    private String path;
+
+    @ApiModelProperty(value = "前端文件路径")
+    private String apiPath;
+
+    @ApiModelProperty(value = "作者")
+    private String author;
+
+    @ApiModelProperty(value = "表前缀")
+    private String prefix;
+
+    @ApiModelProperty(value = "是否覆盖")
+    private Boolean cover = false;
+}

+ 48 - 0
eladmin-generator/src/main/java/me/zhengjie/domain/vo/TableInfo.java

@@ -0,0 +1,48 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.domain.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 表的数据信息
+ * @author Zheng Jie
+ * @date 2019-01-02
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TableInfo {
+
+    /** 表名称 */
+    private Object tableName;
+
+    /** 创建日期 */
+    private Object createTime;
+
+    /** 数据库引擎 */
+    private Object engine;
+
+    /** 编码集 */
+    private Object coding;
+
+    /** 备注 */
+    private Object remark;
+
+
+}

+ 34 - 0
eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoRepository.java

@@ -0,0 +1,34 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.repository;
+
+import me.zhengjie.domain.ColumnInfo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.List;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-01-14
+ */
+public interface ColumnInfoRepository extends JpaRepository<ColumnInfo,Long> {
+
+    /**
+     * 查询表信息
+     * @param tableName 表格名
+     * @return 表信息
+     */
+    List<ColumnInfo> findByTableNameOrderByIdAsc(String tableName);
+}

+ 33 - 0
eladmin-generator/src/main/java/me/zhengjie/repository/GenConfigRepository.java

@@ -0,0 +1,33 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.repository;
+
+import me.zhengjie.domain.GenConfig;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-01-14
+ */
+public interface GenConfigRepository extends JpaRepository<GenConfig,Long> {
+
+    /**
+     * 查询表配置
+     * @param tableName 表名
+     * @return /
+     */
+    GenConfig findByTableName(String tableName);
+}

+ 51 - 0
eladmin-generator/src/main/java/me/zhengjie/rest/GenConfigController.java

@@ -0,0 +1,51 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import me.zhengjie.domain.GenConfig;
+import me.zhengjie.service.GenConfigService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-01-14
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/genConfig")
+@Api(tags = "系统:代码生成器配置管理")
+public class GenConfigController {
+
+    private final GenConfigService genConfigService;
+
+    @ApiOperation("查询")
+    @GetMapping(value = "/{tableName}")
+    public ResponseEntity<Object> query(@PathVariable String tableName){
+        return new ResponseEntity<>(genConfigService.find(tableName), HttpStatus.OK);
+    }
+
+    @ApiOperation("修改")
+    @PutMapping
+    public ResponseEntity<Object> update(@Validated @RequestBody GenConfig genConfig){
+        return new ResponseEntity<>(genConfigService.update(genConfig.getTableName(), genConfig),HttpStatus.OK);
+    }
+}

+ 107 - 0
eladmin-generator/src/main/java/me/zhengjie/rest/GeneratorController.java

@@ -0,0 +1,107 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import me.zhengjie.domain.ColumnInfo;
+import me.zhengjie.exception.BadRequestException;
+import me.zhengjie.service.GenConfigService;
+import me.zhengjie.service.GeneratorService;
+import me.zhengjie.utils.PageUtil;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-01-02
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/generator")
+@Api(tags = "系统:代码生成管理")
+public class GeneratorController {
+
+    private final GeneratorService generatorService;
+    private final GenConfigService genConfigService;
+
+    @Value("${generator.enabled}")
+    private Boolean generatorEnabled;
+
+    @ApiOperation("查询数据库数据")
+    @GetMapping(value = "/tables/all")
+    public ResponseEntity<Object> queryTables(){
+        return new ResponseEntity<>(generatorService.getTables(), HttpStatus.OK);
+    }
+
+    @ApiOperation("查询数据库数据")
+    @GetMapping(value = "/tables")
+    public ResponseEntity<Object> queryTables(@RequestParam(defaultValue = "") String name,
+                                    @RequestParam(defaultValue = "0")Integer page,
+                                    @RequestParam(defaultValue = "10")Integer size){
+        int[] startEnd = PageUtil.transToStartEnd(page, size);
+        return new ResponseEntity<>(generatorService.getTables(name,startEnd), HttpStatus.OK);
+    }
+
+    @ApiOperation("查询字段数据")
+    @GetMapping(value = "/columns")
+    public ResponseEntity<Object> queryColumns(@RequestParam String tableName){
+        List<ColumnInfo> columnInfos = generatorService.getColumns(tableName);
+        return new ResponseEntity<>(PageUtil.toPage(columnInfos,columnInfos.size()), HttpStatus.OK);
+    }
+
+    @ApiOperation("保存字段数据")
+    @PutMapping
+    public ResponseEntity<HttpStatus> save(@RequestBody List<ColumnInfo> columnInfos){
+        generatorService.save(columnInfos);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @ApiOperation("同步字段数据")
+    @PostMapping(value = "sync")
+    public ResponseEntity<HttpStatus> sync(@RequestBody List<String> tables){
+        for (String table : tables) {
+            generatorService.sync(generatorService.getColumns(table), generatorService.query(table));
+        }
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @ApiOperation("生成代码")
+    @PostMapping(value = "/{tableName}/{type}")
+    public ResponseEntity<Object> generator(@PathVariable String tableName, @PathVariable Integer type, HttpServletRequest request, HttpServletResponse response){
+        if(!generatorEnabled && type == 0){
+            throw new BadRequestException("此环境不允许生成代码,请选择预览或者下载查看!");
+        }
+        switch (type){
+            // 生成代码
+            case 0: generatorService.generator(genConfigService.find(tableName), generatorService.getColumns(tableName));
+                    break;
+            // 预览
+            case 1: return generatorService.preview(genConfigService.find(tableName), generatorService.getColumns(tableName));
+            // 打包
+            case 2: generatorService.download(genConfigService.find(tableName), generatorService.getColumns(tableName), request, response);
+                    break;
+            default: throw new BadRequestException("没有这个选项");
+        }
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+}

+ 40 - 0
eladmin-generator/src/main/java/me/zhengjie/service/GenConfigService.java

@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service;
+
+import me.zhengjie.domain.GenConfig;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-01-14
+ */
+public interface GenConfigService {
+
+    /**
+     * 查询表配置
+     * @param tableName 表名
+     * @return 表配置
+     */
+    GenConfig find(String tableName);
+
+    /**
+     * 更新表配置
+     * @param tableName 表名
+     * @param genConfig 表配置
+     * @return 表配置
+     */
+    GenConfig update(String tableName, GenConfig genConfig);
+}

+ 96 - 0
eladmin-generator/src/main/java/me/zhengjie/service/GeneratorService.java

@@ -0,0 +1,96 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service;
+
+import me.zhengjie.domain.GenConfig;
+import me.zhengjie.domain.ColumnInfo;
+import org.springframework.http.ResponseEntity;
+import org.springframework.scheduling.annotation.Async;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-01-02
+ */
+public interface GeneratorService {
+
+    /**
+     * 查询数据库元数据
+     * @param name 表名
+     * @param startEnd 分页参数
+     * @return /
+     */
+    Object getTables(String name, int[] startEnd);
+
+    /**
+     * 得到数据表的元数据
+     * @param name 表名
+     * @return /
+     */
+    List<ColumnInfo> getColumns(String name);
+
+    /**
+     * 同步表数据
+     * @param columnInfos /
+     * @param columnInfoList /
+     */
+    void sync(List<ColumnInfo> columnInfos, List<ColumnInfo> columnInfoList);
+
+    /**
+     * 保持数据
+     * @param columnInfos /
+     */
+    void save(List<ColumnInfo> columnInfos);
+
+    /**
+     * 获取所有table
+     * @return /
+     */
+    Object getTables();
+
+    /**
+     * 代码生成
+     * @param genConfig 配置信息
+     * @param columns 字段信息
+     */
+    void generator(GenConfig genConfig, List<ColumnInfo> columns);
+
+    /**
+     * 预览
+     * @param genConfig 配置信息
+     * @param columns 字段信息
+     * @return /
+     */
+    ResponseEntity<Object> preview(GenConfig genConfig, List<ColumnInfo> columns);
+
+    /**
+     * 打包下载
+     * @param genConfig 配置信息
+     * @param columns 字段信息
+     * @param request /
+     * @param response /
+     */
+    void download(GenConfig genConfig, List<ColumnInfo> columns, HttpServletRequest request, HttpServletResponse response);
+
+    /**
+     * 查询数据库的表字段数据数据
+     * @param table /
+     * @return /
+     */
+    List<ColumnInfo> query(String table);
+}

+ 67 - 0
eladmin-generator/src/main/java/me/zhengjie/service/impl/GenConfigServiceImpl.java

@@ -0,0 +1,67 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import me.zhengjie.domain.GenConfig;
+import me.zhengjie.repository.GenConfigRepository;
+import me.zhengjie.service.GenConfigService;
+import me.zhengjie.utils.StringUtils;
+import org.springframework.stereotype.Service;
+import java.io.File;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-01-14
+ */
+@Service
+@RequiredArgsConstructor
+public class GenConfigServiceImpl implements GenConfigService {
+
+    private final GenConfigRepository genConfigRepository;
+
+    @Override
+    public GenConfig find(String tableName) {
+        GenConfig genConfig = genConfigRepository.findByTableName(tableName);
+        if(genConfig == null){
+            return new GenConfig(tableName);
+        }
+        return genConfig;
+    }
+
+    @Override
+    public GenConfig update(String tableName, GenConfig genConfig) {
+        String separator = File.separator;
+        String[] paths;
+        String symbol = "\\";
+        if (symbol.equals(separator)) {
+            paths = genConfig.getPath().split("\\\\");
+        } else {
+            paths = genConfig.getPath().split(File.separator);
+        }
+        StringBuilder api = new StringBuilder();
+        for (String path : paths) {
+            api.append(path);
+            api.append(separator);
+            if ("src".equals(path)) {
+                api.append("api");
+                break;
+            }
+        }
+        genConfig.setApiPath(api.toString());
+        return genConfigRepository.save(genConfig);
+    }
+}

+ 205 - 0
eladmin-generator/src/main/java/me/zhengjie/service/impl/GeneratorServiceImpl.java

@@ -0,0 +1,205 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ZipUtil;
+import lombok.RequiredArgsConstructor;
+import me.zhengjie.domain.GenConfig;
+import me.zhengjie.domain.ColumnInfo;
+import me.zhengjie.domain.vo.TableInfo;
+import me.zhengjie.exception.BadRequestException;
+import me.zhengjie.repository.ColumnInfoRepository;
+import me.zhengjie.service.GeneratorService;
+import me.zhengjie.utils.FileUtil;
+import me.zhengjie.utils.GenUtil;
+import me.zhengjie.utils.PageUtil;
+import me.zhengjie.utils.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-01-02
+ */
+@Service
+@RequiredArgsConstructor
+public class GeneratorServiceImpl implements GeneratorService {
+    private static final Logger log = LoggerFactory.getLogger(GeneratorServiceImpl.class);
+    @PersistenceContext
+    private EntityManager em;
+
+    private final ColumnInfoRepository columnInfoRepository;
+
+    @Override
+    public Object getTables() {
+        // 使用预编译防止sql注入
+        String sql = "select table_name ,create_time , engine, table_collation, table_comment from information_schema.tables " +
+                "where table_schema = (select database()) " +
+                "order by create_time desc";
+        Query query = em.createNativeQuery(sql);
+        return query.getResultList();
+    }
+
+    @Override
+    public Object getTables(String name, int[] startEnd) {
+        // 使用预编译防止sql注入
+        String sql = "select table_name ,create_time , engine, table_collation, table_comment from information_schema.tables " +
+                "where table_schema = (select database()) " +
+                "and table_name like :table order by create_time desc";
+        Query query = em.createNativeQuery(sql);
+        query.setFirstResult(startEnd[0]);
+        query.setMaxResults(startEnd[1] - startEnd[0]);
+        query.setParameter("table", StringUtils.isNotBlank(name) ? ("%" + name + "%") : "%%");
+        List result = query.getResultList();
+        List<TableInfo> tableInfos = new ArrayList<>();
+        for (Object obj : result) {
+            Object[] arr = (Object[]) obj;
+            tableInfos.add(new TableInfo(arr[0], arr[1], arr[2], arr[3], ObjectUtil.isNotEmpty(arr[4]) ? arr[4] : "-"));
+        }
+        String countSql = "select count(1) from information_schema.tables " +
+                "where table_schema = (select database()) and table_name like :table";
+        Query queryCount = em.createNativeQuery(countSql);
+        queryCount.setParameter("table", StringUtils.isNotBlank(name) ? ("%" + name + "%") : "%%");
+        Object totalElements = queryCount.getSingleResult();
+        return PageUtil.toPage(tableInfos, totalElements);
+    }
+
+    @Override
+    public List<ColumnInfo> getColumns(String tableName) {
+        List<ColumnInfo> columnInfos = columnInfoRepository.findByTableNameOrderByIdAsc(tableName);
+        if (CollectionUtil.isNotEmpty(columnInfos)) {
+            return columnInfos;
+        } else {
+            columnInfos = query(tableName);
+            return columnInfoRepository.saveAll(columnInfos);
+        }
+    }
+
+    @Override
+    public List<ColumnInfo> query(String tableName) {
+        // 使用预编译防止sql注入
+        String sql = "select column_name, is_nullable, data_type, column_comment, column_key, extra from information_schema.columns " +
+                "where table_name = ? and table_schema = (select database()) order by ordinal_position";
+        Query query = em.createNativeQuery(sql);
+        query.setParameter(1, tableName);
+        List result = query.getResultList();
+        List<ColumnInfo> columnInfos = new ArrayList<>();
+        for (Object obj : result) {
+            Object[] arr = (Object[]) obj;
+            columnInfos.add(
+                    new ColumnInfo(
+                            tableName,
+                            arr[0].toString(),
+                            "NO".equals(arr[1]),
+                            arr[2].toString(),
+                            ObjectUtil.isNotNull(arr[3]) ? arr[3].toString() : null,
+                            ObjectUtil.isNotNull(arr[4]) ? arr[4].toString() : null,
+                            ObjectUtil.isNotNull(arr[5]) ? arr[5].toString() : null)
+            );
+        }
+        return columnInfos;
+    }
+
+    @Override
+    public void sync(List<ColumnInfo> columnInfos, List<ColumnInfo> columnInfoList) {
+        // 第一种情况,数据库类字段改变或者新增字段
+        for (ColumnInfo columnInfo : columnInfoList) {
+            // 根据字段名称查找
+            List<ColumnInfo> columns = columnInfos.stream().filter(c -> c.getColumnName().equals(columnInfo.getColumnName())).collect(Collectors.toList());
+            // 如果能找到,就修改部分可能被字段
+            if (CollectionUtil.isNotEmpty(columns)) {
+                ColumnInfo column = columns.get(0);
+                column.setColumnType(columnInfo.getColumnType());
+                column.setExtra(columnInfo.getExtra());
+                column.setKeyType(columnInfo.getKeyType());
+                if (StringUtils.isBlank(column.getRemark())) {
+                    column.setRemark(columnInfo.getRemark());
+                }
+                columnInfoRepository.save(column);
+            } else {
+                // 如果找不到,则保存新字段信息
+                columnInfoRepository.save(columnInfo);
+            }
+        }
+        // 第二种情况,数据库字段删除了
+        for (ColumnInfo columnInfo : columnInfos) {
+            // 根据字段名称查找
+            List<ColumnInfo> columns = columnInfoList.stream().filter(c -> c.getColumnName().equals(columnInfo.getColumnName())).collect(Collectors.toList());
+            // 如果找不到,就代表字段被删除了,则需要删除该字段
+            if (CollectionUtil.isEmpty(columns)) {
+                columnInfoRepository.delete(columnInfo);
+            }
+        }
+    }
+
+    @Override
+    public void save(List<ColumnInfo> columnInfos) {
+        columnInfoRepository.saveAll(columnInfos);
+    }
+
+    @Override
+    public void generator(GenConfig genConfig, List<ColumnInfo> columns) {
+        if (genConfig.getId() == null) {
+            throw new BadRequestException("请先配置生成器");
+        }
+        try {
+            GenUtil.generatorCode(columns, genConfig);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+            throw new BadRequestException("生成失败,请手动处理已生成的文件");
+        }
+    }
+
+    @Override
+    public ResponseEntity<Object> preview(GenConfig genConfig, List<ColumnInfo> columns) {
+        if (genConfig.getId() == null) {
+            throw new BadRequestException("请先配置生成器");
+        }
+        List<Map<String, Object>> genList = GenUtil.preview(columns, genConfig);
+        return new ResponseEntity<>(genList, HttpStatus.OK);
+    }
+
+    @Override
+    public void download(GenConfig genConfig, List<ColumnInfo> columns, HttpServletRequest request, HttpServletResponse response) {
+        if (genConfig.getId() == null) {
+            throw new BadRequestException("请先配置生成器");
+        }
+        try {
+            File file = new File(GenUtil.download(columns, genConfig));
+            String zipPath = file.getPath() + ".zip";
+            ZipUtil.zip(file.getPath(), zipPath);
+            FileUtil.downloadFile(request, response, new File(zipPath), true);
+        } catch (IOException e) {
+            throw new BadRequestException("打包失败");
+        }
+    }
+}

+ 54 - 0
eladmin-generator/src/main/java/me/zhengjie/utils/ColUtil.java

@@ -0,0 +1,54 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import org.apache.commons.configuration.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * sql字段转java
+ *
+ * @author Zheng Jie
+ * @date 2019-01-03
+ */
+public class ColUtil {
+    private static final Logger log = LoggerFactory.getLogger(ColUtil.class);
+
+    /**
+     * 转换mysql数据类型为java数据类型
+     *
+     * @param type 数据库字段类型
+     * @return String
+     */
+    static String cloToJava(String type) {
+        Configuration config = getConfig();
+        assert config != null;
+        return config.getString(type, "unknowType");
+    }
+
+    /**
+     * 获取配置信息
+     */
+    public static PropertiesConfiguration getConfig() {
+        try {
+            return new PropertiesConfiguration("generator.properties");
+        } catch (ConfigurationException e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+}

+ 421 - 0
eladmin-generator/src/main/java/me/zhengjie/utils/GenUtil.java

@@ -0,0 +1,421 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.utils;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.template.*;
+import lombok.extern.slf4j.Slf4j;
+import me.zhengjie.domain.GenConfig;
+import me.zhengjie.domain.ColumnInfo;
+import org.springframework.util.ObjectUtils;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.time.LocalDate;
+import java.util.*;
+
+import static me.zhengjie.utils.FileUtil.SYS_TEM_DIR;
+
+/**
+ * 代码生成
+ *
+ * @author Zheng Jie
+ * @date 2019-01-02
+ */
+@Slf4j
+@SuppressWarnings({"unchecked", "all"})
+public class GenUtil {
+
+    private static final String TIMESTAMP = "Timestamp";
+
+    private static final String BIGDECIMAL = "BigDecimal";
+
+    public static final String PK = "PRI";
+
+    public static final String EXTRA = "auto_increment";
+
+    /**
+     * 获取后端代码模板名称
+     *
+     * @return List
+     */
+    private static List<String> getAdminTemplateNames() {
+        List<String> templateNames = new ArrayList<>();
+        templateNames.add("Entity");
+        templateNames.add("Dto");
+        templateNames.add("Mapper");
+        templateNames.add("Controller");
+        templateNames.add("QueryCriteria");
+        templateNames.add("Service");
+        templateNames.add("ServiceImpl");
+        templateNames.add("Repository");
+        return templateNames;
+    }
+
+    /**
+     * 获取前端代码模板名称
+     *
+     * @return List
+     */
+    private static List<String> getFrontTemplateNames() {
+        List<String> templateNames = new ArrayList<>();
+        templateNames.add("index");
+        templateNames.add("api");
+        return templateNames;
+    }
+
+    public static List<Map<String, Object>> preview(List<ColumnInfo> columns, GenConfig genConfig) {
+        Map<String, Object> genMap = getGenMap(columns, genConfig);
+        List<Map<String, Object>> genList = new ArrayList<>();
+        // 获取后端模版
+        List<String> templates = getAdminTemplateNames();
+        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
+        for (String templateName : templates) {
+            Map<String, Object> map = new HashMap<>(1);
+            Template template = engine.getTemplate("generator/admin/" + templateName + ".ftl");
+            map.put("content", template.render(genMap));
+            map.put("name", templateName);
+            genList.add(map);
+        }
+        // 获取前端模版
+        templates = getFrontTemplateNames();
+        for (String templateName : templates) {
+            Map<String, Object> map = new HashMap<>(1);
+            Template template = engine.getTemplate("generator/front/" + templateName + ".ftl");
+            map.put(templateName, template.render(genMap));
+            map.put("content", template.render(genMap));
+            map.put("name", templateName);
+            genList.add(map);
+        }
+        return genList;
+    }
+
+    public static String download(List<ColumnInfo> columns, GenConfig genConfig) throws IOException {
+        // 拼接的路径:/tmpeladmin-gen-temp/,这个路径在Linux下需要root用户才有权限创建,非root用户会权限错误而失败,更改为: /tmp/eladmin-gen-temp/
+        // String tempPath =SYS_TEM_DIR + "eladmin-gen-temp" + File.separator + genConfig.getTableName() + File.separator;
+        String tempPath = SYS_TEM_DIR + "eladmin-gen-temp" + File.separator + genConfig.getTableName() + File.separator;
+        Map<String, Object> genMap = getGenMap(columns, genConfig);
+        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
+        // 生成后端代码
+        List<String> templates = getAdminTemplateNames();
+        for (String templateName : templates) {
+            Template template = engine.getTemplate("generator/admin/" + templateName + ".ftl");
+            String filePath = getAdminFilePath(templateName, genConfig, genMap.get("className").toString(), tempPath + "eladmin" + File.separator);
+            assert filePath != null;
+            File file = new File(filePath);
+            // 如果非覆盖生成
+            if (!genConfig.getCover() && FileUtil.exist(file)) {
+                continue;
+            }
+            // 生成代码
+            genFile(file, template, genMap);
+        }
+        // 生成前端代码
+        templates = getFrontTemplateNames();
+        for (String templateName : templates) {
+            Template template = engine.getTemplate("generator/front/" + templateName + ".ftl");
+            String path = tempPath + "eladmin-web" + File.separator;
+            String apiPath = path + "src" + File.separator + "api" + File.separator;
+            String srcPath = path + "src" + File.separator + "views" + File.separator + genMap.get("changeClassName").toString() + File.separator;
+            String filePath = getFrontFilePath(templateName, apiPath, srcPath, genMap.get("changeClassName").toString());
+            assert filePath != null;
+            File file = new File(filePath);
+            // 如果非覆盖生成
+            if (!genConfig.getCover() && FileUtil.exist(file)) {
+                continue;
+            }
+            // 生成代码
+            genFile(file, template, genMap);
+        }
+        return tempPath;
+    }
+
+    public static void generatorCode(List<ColumnInfo> columnInfos, GenConfig genConfig) throws IOException {
+        Map<String, Object> genMap = getGenMap(columnInfos, genConfig);
+        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
+        // 生成后端代码
+        List<String> templates = getAdminTemplateNames();
+        for (String templateName : templates) {
+            Template template = engine.getTemplate("generator/admin/" + templateName + ".ftl");
+            String rootPath = System.getProperty("user.dir");
+            String filePath = getAdminFilePath(templateName, genConfig, genMap.get("className").toString(), rootPath);
+
+            assert filePath != null;
+            File file = new File(filePath);
+
+            // 如果非覆盖生成
+            if (!genConfig.getCover() && FileUtil.exist(file)) {
+                continue;
+            }
+            // 生成代码
+            genFile(file, template, genMap);
+        }
+
+        // 生成前端代码
+        templates = getFrontTemplateNames();
+        for (String templateName : templates) {
+            Template template = engine.getTemplate("generator/front/" + templateName + ".ftl");
+            String filePath = getFrontFilePath(templateName, genConfig.getApiPath(), genConfig.getPath(), genMap.get("changeClassName").toString());
+
+            assert filePath != null;
+            File file = new File(filePath);
+
+            // 如果非覆盖生成
+            if (!genConfig.getCover() && FileUtil.exist(file)) {
+                continue;
+            }
+            // 生成代码
+            genFile(file, template, genMap);
+        }
+    }
+
+    // 获取模版数据
+    private static Map<String, Object> getGenMap(List<ColumnInfo> columnInfos, GenConfig genConfig) {
+        // 存储模版字段数据
+        Map<String, Object> genMap = new HashMap<>(16);
+        // 接口别名
+        genMap.put("apiAlias", genConfig.getApiAlias());
+        // 包名称
+        genMap.put("package", genConfig.getPack());
+        // 模块名称
+        genMap.put("moduleName", genConfig.getModuleName());
+        // 作者
+        genMap.put("author", genConfig.getAuthor());
+        // 创建日期
+        genMap.put("date", LocalDate.now().toString());
+        // 表名
+        genMap.put("tableName", genConfig.getTableName());
+        // 大写开头的类名
+        String className = StringUtils.toCapitalizeCamelCase(genConfig.getTableName());
+        // 小写开头的类名
+        String changeClassName = StringUtils.toCamelCase(genConfig.getTableName());
+        // 判断是否去除表前缀
+        if (StringUtils.isNotEmpty(genConfig.getPrefix())) {
+            className = StringUtils.toCapitalizeCamelCase(StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix()));
+            changeClassName = StringUtils.toCamelCase(StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix()));
+            changeClassName = StringUtils.uncapitalize(changeClassName);
+        }
+        // 保存类名
+        genMap.put("className", className);
+        // 保存小写开头的类名
+        genMap.put("changeClassName", changeClassName);
+        // 存在 Timestamp 字段
+        genMap.put("hasTimestamp", false);
+        // 查询类中存在 Timestamp 字段
+        genMap.put("queryHasTimestamp", false);
+        // 存在 BigDecimal 字段
+        genMap.put("hasBigDecimal", false);
+        // 查询类中存在 BigDecimal 字段
+        genMap.put("queryHasBigDecimal", false);
+        // 是否需要创建查询
+        genMap.put("hasQuery", false);
+        // 自增主键
+        genMap.put("auto", false);
+        // 存在字典
+        genMap.put("hasDict", false);
+        // 存在日期注解
+        genMap.put("hasDateAnnotation", false);
+        // 保存字段信息
+        List<Map<String, Object>> columns = new ArrayList<>();
+        // 保存查询字段的信息
+        List<Map<String, Object>> queryColumns = new ArrayList<>();
+        // 存储字典信息
+        List<String> dicts = new ArrayList<>();
+        // 存储 between 信息
+        List<Map<String, Object>> betweens = new ArrayList<>();
+        // 存储不为空的字段信息
+        List<Map<String, Object>> isNotNullColumns = new ArrayList<>();
+
+        for (ColumnInfo column : columnInfos) {
+            Map<String, Object> listMap = new HashMap<>(16);
+            // 字段描述
+            listMap.put("remark", column.getRemark());
+            // 字段类型
+            listMap.put("columnKey", column.getKeyType());
+            // 主键类型
+            String colType = ColUtil.cloToJava(column.getColumnType());
+            // 小写开头的字段名
+            String changeColumnName = StringUtils.toCamelCase(column.getColumnName());
+            // 大写开头的字段名
+            String capitalColumnName = StringUtils.toCapitalizeCamelCase(column.getColumnName());
+            if (PK.equals(column.getKeyType())) {
+                // 存储主键类型
+                genMap.put("pkColumnType", colType);
+                // 存储小写开头的字段名
+                genMap.put("pkChangeColName", changeColumnName);
+                // 存储大写开头的字段名
+                genMap.put("pkCapitalColName", capitalColumnName);
+            }
+            // 是否存在 Timestamp 类型的字段
+            if (TIMESTAMP.equals(colType)) {
+                genMap.put("hasTimestamp", true);
+            }
+            // 是否存在 BigDecimal 类型的字段
+            if (BIGDECIMAL.equals(colType)) {
+                genMap.put("hasBigDecimal", true);
+            }
+            // 主键是否自增
+            if (EXTRA.equals(column.getExtra())) {
+                genMap.put("auto", true);
+            }
+            // 主键存在字典
+            if (StringUtils.isNotBlank(column.getDictName())) {
+                genMap.put("hasDict", true);
+                dicts.add(column.getDictName());
+            }
+
+            // 存储字段类型
+            listMap.put("columnType", colType);
+            // 存储字原始段名称
+            listMap.put("columnName", column.getColumnName());
+            // 不为空
+            listMap.put("istNotNull", column.getNotNull());
+            // 字段列表显示
+            listMap.put("columnShow", column.getListShow());
+            // 表单显示
+            listMap.put("formShow", column.getFormShow());
+            // 表单组件类型
+            listMap.put("formType", StringUtils.isNotBlank(column.getFormType()) ? column.getFormType() : "Input");
+            // 小写开头的字段名称
+            listMap.put("changeColumnName", changeColumnName);
+            //大写开头的字段名称
+            listMap.put("capitalColumnName", capitalColumnName);
+            // 字典名称
+            listMap.put("dictName", column.getDictName());
+            // 日期注解
+            listMap.put("dateAnnotation", column.getDateAnnotation());
+            if (StringUtils.isNotBlank(column.getDateAnnotation())) {
+                genMap.put("hasDateAnnotation", true);
+            }
+            // 添加非空字段信息
+            if (column.getNotNull()) {
+                isNotNullColumns.add(listMap);
+            }
+            // 判断是否有查询,如有则把查询的字段set进columnQuery
+            if (!StringUtils.isBlank(column.getQueryType())) {
+                // 查询类型
+                listMap.put("queryType", column.getQueryType());
+                // 是否存在查询
+                genMap.put("hasQuery", true);
+                if (TIMESTAMP.equals(colType)) {
+                    // 查询中存储 Timestamp 类型
+                    genMap.put("queryHasTimestamp", true);
+                }
+                if (BIGDECIMAL.equals(colType)) {
+                    // 查询中存储 BigDecimal 类型
+                    genMap.put("queryHasBigDecimal", true);
+                }
+                if ("between".equalsIgnoreCase(column.getQueryType())) {
+                    betweens.add(listMap);
+                } else {
+                    // 添加到查询列表中
+                    queryColumns.add(listMap);
+                }
+            }
+            // 添加到字段列表中
+            columns.add(listMap);
+        }
+        // 保存字段列表
+        genMap.put("columns", columns);
+        // 保存查询列表
+        genMap.put("queryColumns", queryColumns);
+        // 保存字段列表
+        genMap.put("dicts", dicts);
+        // 保存查询列表
+        genMap.put("betweens", betweens);
+        // 保存非空字段信息
+        genMap.put("isNotNullColumns", isNotNullColumns);
+        return genMap;
+    }
+
+    /**
+     * 定义后端文件路径以及名称
+     */
+    private static String getAdminFilePath(String templateName, GenConfig genConfig, String className, String rootPath) {
+        String projectPath = rootPath + File.separator + genConfig.getModuleName();
+        String packagePath = projectPath + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator;
+        if (!ObjectUtils.isEmpty(genConfig.getPack())) {
+            packagePath += genConfig.getPack().replace(".", File.separator) + File.separator;
+        }
+
+        if ("Entity".equals(templateName)) {
+            return packagePath + "domain" + File.separator + className + ".java";
+        }
+
+        if ("Controller".equals(templateName)) {
+            return packagePath + "rest" + File.separator + className + "Controller.java";
+        }
+
+        if ("Service".equals(templateName)) {
+            return packagePath + "service" + File.separator + className + "Service.java";
+        }
+
+        if ("ServiceImpl".equals(templateName)) {
+            return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
+        }
+
+        if ("Dto".equals(templateName)) {
+            return packagePath + "service" + File.separator + "dto" + File.separator + className + "Dto.java";
+        }
+
+        if ("QueryCriteria".equals(templateName)) {
+            return packagePath + "service" + File.separator + "dto" + File.separator + className + "QueryCriteria.java";
+        }
+
+        if ("Mapper".equals(templateName)) {
+            return packagePath + "service" + File.separator + "mapstruct" + File.separator + className + "Mapper.java";
+        }
+
+        if ("Repository".equals(templateName)) {
+            return packagePath + "repository" + File.separator + className + "Repository.java";
+        }
+
+        return null;
+    }
+
+    /**
+     * 定义前端文件路径以及名称
+     */
+    private static String getFrontFilePath(String templateName, String apiPath, String path, String apiName) {
+
+        if ("api".equals(templateName)) {
+            return apiPath + File.separator + apiName + ".js";
+        }
+
+        if ("index".equals(templateName)) {
+            return path + File.separator + "index.vue";
+        }
+
+        return null;
+    }
+
+    private static void genFile(File file, Template template, Map<String, Object> map) throws IOException {
+        // 生成目标文件
+        Writer writer = null;
+        try {
+            FileUtil.touch(file);
+            writer = new FileWriter(file);
+            template.render(map, writer);
+        } catch (TemplateException | IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            assert writer != null;
+            writer.close();
+        }
+    }
+}

+ 22 - 0
eladmin-logging/pom.xml

@@ -0,0 +1,22 @@
+<?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>eladmin</artifactId>
+        <groupId>me.zhengjie</groupId>
+        <version>2.6</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>eladmin-logging</artifactId>
+    <name>日志模块</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>me.zhengjie</groupId>
+            <artifactId>eladmin-common</artifactId>
+            <version>2.6</version>
+        </dependency>
+    </dependencies>
+</project>

+ 31 - 0
eladmin-logging/src/main/java/me/zhengjie/annotation/Log.java

@@ -0,0 +1,31 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-24
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Log {
+    String value() default "";
+}

+ 98 - 0
eladmin-logging/src/main/java/me/zhengjie/aspect/LogAspect.java

@@ -0,0 +1,98 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.aspect;
+
+import lombok.extern.slf4j.Slf4j;
+import me.zhengjie.domain.Log;
+import me.zhengjie.service.LogService;
+import me.zhengjie.utils.RequestHolder;
+import me.zhengjie.utils.SecurityUtils;
+import me.zhengjie.utils.StringUtils;
+import me.zhengjie.utils.ThrowableUtil;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-24
+ */
+@Component
+@Aspect
+@Slf4j
+public class LogAspect {
+
+    private final LogService logService;
+
+    ThreadLocal<Long> currentTime = new ThreadLocal<>();
+
+    public LogAspect(LogService logService) {
+        this.logService = logService;
+    }
+
+    /**
+     * 配置切入点
+     */
+    @Pointcut("@annotation(me.zhengjie.annotation.Log)")
+    public void logPointcut() {
+        // 该方法无方法体,主要为了让同类中其他方法使用此切入点
+    }
+
+    /**
+     * 配置环绕通知,使用在方法logPointcut()上注册的切入点
+     *
+     * @param joinPoint join point for advice
+     */
+    @Around("logPointcut()")
+    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
+        Object result;
+        currentTime.set(System.currentTimeMillis());
+        result = joinPoint.proceed();
+        Log log = new Log("INFO",System.currentTimeMillis() - currentTime.get());
+        currentTime.remove();
+        HttpServletRequest request = RequestHolder.getHttpServletRequest();
+        logService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request),joinPoint, log);
+        return result;
+    }
+
+    /**
+     * 配置异常通知
+     *
+     * @param joinPoint join point for advice
+     * @param e exception
+     */
+    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
+    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
+        Log log = new Log("ERROR",System.currentTimeMillis() - currentTime.get());
+        currentTime.remove();
+        log.setExceptionDetail(ThrowableUtil.getStackTrace(e).getBytes());
+        HttpServletRequest request = RequestHolder.getHttpServletRequest();
+        logService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request), (ProceedingJoinPoint)joinPoint, log);
+    }
+
+    public String getUsername() {
+        try {
+            return SecurityUtils.getCurrentUsername();
+        }catch (Exception e){
+            return "";
+        }
+    }
+}

+ 80 - 0
eladmin-logging/src/main/java/me/zhengjie/domain/Log.java

@@ -0,0 +1,80 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.domain;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.hibernate.annotations.CreationTimestamp;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-24
+ */
+@Entity
+@Getter
+@Setter
+@Table(name = "sys_log")
+@NoArgsConstructor
+public class Log  implements Serializable {
+
+    @Id
+    @Column(name = "log_id")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    /** 操作用户 */
+    private String username;
+
+    /** 描述 */
+    private String description;
+
+    /** 方法名 */
+    private String method;
+
+    /** 参数 */
+    private String params;
+
+    /** 日志类型 */
+    private String logType;
+
+    /** 请求ip */
+    private String requestIp;
+
+    /** 地址 */
+    private String address;
+
+    /** 浏览器  */
+    private String browser;
+
+    /** 请求耗时 */
+    private Long time;
+
+    /** 异常详细  */
+    private byte[] exceptionDetail;
+
+    /** 创建日期 */
+    @CreationTimestamp
+    private Timestamp createTime;
+
+    public Log(String logType, Long time) {
+        this.logType = logType;
+        this.time = time;
+    }
+}

+ 39 - 0
eladmin-logging/src/main/java/me/zhengjie/repository/LogRepository.java

@@ -0,0 +1,39 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.repository;
+
+import me.zhengjie.domain.Log;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-24
+ */
+@Repository
+public interface LogRepository extends JpaRepository<Log,Long>, JpaSpecificationExecutor<Log> {
+
+    /**
+     * 根据日志类型删除信息
+     * @param logType 日志类型
+     */
+    @Modifying
+    @Query(value = "delete from sys_log where log_type = ?1", nativeQuery = true)
+    void deleteByLogType(String logType);
+}

+ 109 - 0
eladmin-logging/src/main/java/me/zhengjie/rest/LogController.java

@@ -0,0 +1,109 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import me.zhengjie.annotation.Log;
+import me.zhengjie.service.LogService;
+import me.zhengjie.service.dto.LogQueryCriteria;
+import me.zhengjie.utils.SecurityUtils;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-24
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/logs")
+@Api(tags = "系统:日志管理")
+public class LogController {
+
+    private final LogService logService;
+
+    @Log("导出数据")
+    @ApiOperation("导出数据")
+    @GetMapping(value = "/download")
+    @PreAuthorize("@el.check()")
+    public void download(HttpServletResponse response, LogQueryCriteria criteria) throws IOException {
+        criteria.setLogType("INFO");
+        logService.download(logService.queryAll(criteria), response);
+    }
+
+    @Log("导出错误数据")
+    @ApiOperation("导出错误数据")
+    @GetMapping(value = "/error/download")
+    @PreAuthorize("@el.check()")
+    public void downloadErrorLog(HttpServletResponse response, LogQueryCriteria criteria) throws IOException {
+        criteria.setLogType("ERROR");
+        logService.download(logService.queryAll(criteria), response);
+    }
+    @GetMapping
+    @ApiOperation("日志查询")
+    @PreAuthorize("@el.check()")
+    public ResponseEntity<Object> query(LogQueryCriteria criteria, Pageable pageable){
+        criteria.setLogType("INFO");
+        return new ResponseEntity<>(logService.queryAll(criteria,pageable), HttpStatus.OK);
+    }
+
+    @GetMapping(value = "/user")
+    @ApiOperation("用户日志查询")
+    public ResponseEntity<Object> queryUserLog(LogQueryCriteria criteria, Pageable pageable){
+        criteria.setLogType("INFO");
+        criteria.setBlurry(SecurityUtils.getCurrentUsername());
+        return new ResponseEntity<>(logService.queryAllByUser(criteria,pageable), HttpStatus.OK);
+    }
+
+    @GetMapping(value = "/error")
+    @ApiOperation("错误日志查询")
+    @PreAuthorize("@el.check()")
+    public ResponseEntity<Object> queryErrorLog(LogQueryCriteria criteria, Pageable pageable){
+        criteria.setLogType("ERROR");
+        return new ResponseEntity<>(logService.queryAll(criteria,pageable), HttpStatus.OK);
+    }
+
+    @GetMapping(value = "/error/{id}")
+    @ApiOperation("日志异常详情查询")
+    @PreAuthorize("@el.check()")
+    public ResponseEntity<Object> queryErrorLogs(@PathVariable Long id){
+        return new ResponseEntity<>(logService.findByErrDetail(id), HttpStatus.OK);
+    }
+    @DeleteMapping(value = "/del/error")
+    @Log("删除所有ERROR日志")
+    @ApiOperation("删除所有ERROR日志")
+    @PreAuthorize("@el.check()")
+    public ResponseEntity<Object> delAllErrorLog(){
+        logService.delAllByError();
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @DeleteMapping(value = "/del/info")
+    @Log("删除所有INFO日志")
+    @ApiOperation("删除所有INFO日志")
+    @PreAuthorize("@el.check()")
+    public ResponseEntity<Object> delAllInfoLog(){
+        logService.delAllByInfo();
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+}

+ 92 - 0
eladmin-logging/src/main/java/me/zhengjie/service/LogService.java

@@ -0,0 +1,92 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service;
+
+import me.zhengjie.domain.Log;
+import me.zhengjie.service.dto.LogQueryCriteria;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.springframework.data.domain.Pageable;
+import org.springframework.scheduling.annotation.Async;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-24
+ */
+public interface LogService {
+
+    /**
+     * 分页查询
+     * @param criteria 查询条件
+     * @param pageable 分页参数
+     * @return /
+     */
+    Object queryAll(LogQueryCriteria criteria, Pageable pageable);
+
+    /**
+     * 查询全部数据
+     * @param criteria 查询条件
+     * @return /
+     */
+    List<Log> queryAll(LogQueryCriteria criteria);
+
+    /**
+     * 查询用户日志
+     * @param criteria 查询条件
+     * @param pageable 分页参数
+     * @return -
+     */
+    Object queryAllByUser(LogQueryCriteria criteria, Pageable pageable);
+
+    /**
+     * 保存日志数据
+     * @param username 用户
+     * @param browser 浏览器
+     * @param ip 请求IP
+     * @param joinPoint /
+     * @param log 日志实体
+     */
+    @Async
+    void save(String username, String browser, String ip, ProceedingJoinPoint joinPoint, Log log);
+
+    /**
+     * 查询异常详情
+     * @param id 日志ID
+     * @return Object
+     */
+    Object findByErrDetail(Long id);
+
+    /**
+     * 导出日志
+     * @param logs 待导出的数据
+     * @param response /
+     * @throws IOException /
+     */
+    void download(List<Log> logs, HttpServletResponse response) throws IOException;
+
+    /**
+     * 删除所有错误日志
+     */
+    void delAllByError();
+
+    /**
+     * 删除所有INFO日志
+     */
+    void delAllByInfo();
+}

+ 46 - 0
eladmin-logging/src/main/java/me/zhengjie/service/dto/LogErrorDTO.java

@@ -0,0 +1,46 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service.dto;
+
+import lombok.Data;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+* @author Zheng Jie
+* @date 2019-5-22
+*/
+@Data
+public class LogErrorDTO implements Serializable {
+
+    private Long id;
+
+    private String username;
+
+    private String description;
+
+    private String method;
+
+    private String params;
+
+    private String browser;
+
+    private String requestIp;
+
+    private String address;
+
+    private Timestamp createTime;
+}

+ 39 - 0
eladmin-logging/src/main/java/me/zhengjie/service/dto/LogQueryCriteria.java

@@ -0,0 +1,39 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service.dto;
+
+import lombok.Data;
+import me.zhengjie.annotation.Query;
+import java.sql.Timestamp;
+import java.util.List;
+
+/**
+ * 日志查询类
+ * @author Zheng Jie
+ * @date 2019-6-4 09:23:07
+ */
+@Data
+public class LogQueryCriteria {
+
+    @Query(blurry = "username,description,address,requestIp,method,params")
+    private String blurry;
+
+    @Query
+    private String logType;
+
+    @Query(type = Query.Type.BETWEEN)
+    private List<Timestamp> createTime;
+}

+ 40 - 0
eladmin-logging/src/main/java/me/zhengjie/service/dto/LogSmallDTO.java

@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service.dto;
+
+import lombok.Data;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-5-22
+ */
+@Data
+public class LogSmallDTO implements Serializable {
+
+    private String description;
+
+    private String requestIp;
+
+    private Long time;
+
+    private String address;
+
+    private String browser;
+
+    private Timestamp createTime;
+}

+ 172 - 0
eladmin-logging/src/main/java/me/zhengjie/service/impl/LogServiceImpl.java

@@ -0,0 +1,172 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service.impl;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
+import lombok.RequiredArgsConstructor;
+import me.zhengjie.domain.Log;
+import me.zhengjie.repository.LogRepository;
+import me.zhengjie.service.LogService;
+import me.zhengjie.service.dto.LogQueryCriteria;
+import me.zhengjie.service.mapstruct.LogErrorMapper;
+import me.zhengjie.service.mapstruct.LogSmallMapper;
+import me.zhengjie.utils.*;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.*;
+
+/**
+ * @author Zheng Jie
+ * @date 2018-11-24
+ */
+@Service
+@RequiredArgsConstructor
+public class LogServiceImpl implements LogService {
+    private static final Logger log = LoggerFactory.getLogger(LogServiceImpl.class);
+    private final LogRepository logRepository;
+    private final LogErrorMapper logErrorMapper;
+    private final LogSmallMapper logSmallMapper;
+
+    @Override
+    public Object queryAll(LogQueryCriteria criteria, Pageable pageable) {
+        Page<Log> page = logRepository.findAll(((root, criteriaQuery, cb) -> QueryHelp.getPredicate(root, criteria, cb)), pageable);
+        String status = "ERROR";
+        if (status.equals(criteria.getLogType())) {
+            return PageUtil.toPage(page.map(logErrorMapper::toDto));
+        }
+        return page;
+    }
+
+    @Override
+    public List<Log> queryAll(LogQueryCriteria criteria) {
+        return logRepository.findAll(((root, criteriaQuery, cb) -> QueryHelp.getPredicate(root, criteria, cb)));
+    }
+
+    @Override
+    public Object queryAllByUser(LogQueryCriteria criteria, Pageable pageable) {
+        Page<Log> page = logRepository.findAll(((root, criteriaQuery, cb) -> QueryHelp.getPredicate(root, criteria, cb)), pageable);
+        return PageUtil.toPage(page.map(logSmallMapper::toDto));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void save(String username, String browser, String ip, ProceedingJoinPoint joinPoint, Log log) {
+
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+        me.zhengjie.annotation.Log aopLog = method.getAnnotation(me.zhengjie.annotation.Log.class);
+
+        // 方法路径
+        String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";
+
+        // 描述
+        if (log != null) {
+            log.setDescription(aopLog.value());
+        }
+        assert log != null;
+        log.setRequestIp(ip);
+
+        log.setAddress(StringUtils.getCityInfo(log.getRequestIp()));
+        log.setMethod(methodName);
+        log.setUsername(username);
+        log.setParams(getParameter(method, joinPoint.getArgs()));
+        log.setBrowser(browser);
+        logRepository.save(log);
+    }
+
+    /**
+     * 根据方法和传入的参数获取请求参数
+     */
+    private String getParameter(Method method, Object[] args) {
+        List<Object> argList = new ArrayList<>();
+        Parameter[] parameters = method.getParameters();
+        for (int i = 0; i < parameters.length; i++) {
+            //将RequestBody注解修饰的参数作为请求参数
+            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
+            if (requestBody != null) {
+                argList.add(args[i]);
+            }
+            //将RequestParam注解修饰的参数作为请求参数
+            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
+            if (requestParam != null) {
+                Map<String, Object> map = new HashMap<>();
+                String key = parameters[i].getName();
+                if (!StringUtils.isEmpty(requestParam.value())) {
+                    key = requestParam.value();
+                }
+                map.put(key, args[i]);
+                argList.add(map);
+            }
+        }
+        if (argList.size() == 0) {
+            return "";
+        }
+        return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : JSONUtil.toJsonStr(argList);
+    }
+
+    @Override
+    public Object findByErrDetail(Long id) {
+        Log log = logRepository.findById(id).orElseGet(Log::new);
+        ValidationUtil.isNull(log.getId(), "Log", "id", id);
+        byte[] details = log.getExceptionDetail();
+        return Dict.create().set("exception", new String(ObjectUtil.isNotNull(details) ? details : "".getBytes()));
+    }
+
+    @Override
+    public void download(List<Log> logs, HttpServletResponse response) throws IOException {
+        List<Map<String, Object>> list = new ArrayList<>();
+        for (Log log : logs) {
+            Map<String, Object> map = new LinkedHashMap<>();
+            map.put("用户名", log.getUsername());
+            map.put("IP", log.getRequestIp());
+            map.put("IP来源", log.getAddress());
+            map.put("描述", log.getDescription());
+            map.put("浏览器", log.getBrowser());
+            map.put("请求耗时/毫秒", log.getTime());
+            map.put("异常详情", new String(ObjectUtil.isNotNull(log.getExceptionDetail()) ? log.getExceptionDetail() : "".getBytes()));
+            map.put("创建日期", log.getCreateTime());
+            list.add(map);
+        }
+        FileUtil.downloadExcel(list, response);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void delAllByError() {
+        logRepository.deleteByLogType("ERROR");
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void delAllByInfo() {
+        logRepository.deleteByLogType("INFO");
+    }
+}

+ 31 - 0
eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogErrorMapper.java

@@ -0,0 +1,31 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service.mapstruct;
+
+import me.zhengjie.base.BaseMapper;
+import me.zhengjie.domain.Log;
+import me.zhengjie.service.dto.LogErrorDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-5-22
+ */
+@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface LogErrorMapper extends BaseMapper<LogErrorDTO, Log> {
+
+}

+ 31 - 0
eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogSmallMapper.java

@@ -0,0 +1,31 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.service.mapstruct;
+
+import me.zhengjie.base.BaseMapper;
+import me.zhengjie.domain.Log;
+import me.zhengjie.service.dto.LogSmallDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * @author Zheng Jie
+ * @date 2019-5-22
+ */
+@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface LogSmallMapper extends BaseMapper<LogSmallDTO, Log> {
+
+}

二進制
eladmin-system/.DS_Store


+ 147 - 0
eladmin-system/pom.xml

@@ -0,0 +1,147 @@
+<?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>eladmin</artifactId>
+        <groupId>me.zhengjie</groupId>
+        <version>2.6</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>eladmin-system</artifactId>
+    <name>核心模块</name>
+
+    <properties>
+        <jjwt.version>0.11.1</jjwt.version>
+        <!-- oshi监控需要指定jna版本, 问题详见 https://github.com/oshi/oshi/issues/1040 -->
+        <jna.version>5.8.0</jna.version>
+    </properties>
+
+    <dependencies>
+        <!-- 代码生成模块 -->
+        <dependency>
+            <groupId>me.zhengjie</groupId>
+            <artifactId>eladmin-generator</artifactId>
+            <version>2.6</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>me.zhengjie</groupId>
+                    <artifactId>eladmin-common</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- rabbitmq -->
+        <dependency>
+            <groupId>com.rabbitmq</groupId>
+            <artifactId>amqp-client</artifactId>
+            <version>3.4.1</version>
+        </dependency>
+        <!-- rocketMq -->
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-spring-boot-starter</artifactId>
+            <version>2.1.1</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-webmvc</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- tools 模块包含了 common 和 logging 模块 -->
+        <dependency>
+            <groupId>me.zhengjie</groupId>
+            <artifactId>eladmin-tools</artifactId>
+            <version>2.6</version>
+        </dependency>
+
+        <!-- Spring boot websocket -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-websocket</artifactId>
+		</dependency>
+
+        <!-- jwt -->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>${jjwt.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>${jjwt.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>${jjwt.version}</version>
+        </dependency>
+
+        <!-- quartz -->
+        <dependency>
+            <groupId>org.quartz-scheduler</groupId>
+            <artifactId>quartz</artifactId>
+        </dependency>
+
+        <!-- linux的管理 -->
+		<dependency>
+			<groupId>ch.ethz.ganymed</groupId>
+			<artifactId>ganymed-ssh2</artifactId>
+			<version>build210</version>
+		</dependency>
+		<dependency>
+			<groupId>com.jcraft</groupId>
+			<artifactId>jsch</artifactId>
+			<version>0.1.55</version>
+		</dependency>
+
+        <!-- 获取系统信息 -->
+        <dependency>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+            <version>5.7.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.rabbitmq</groupId>
+            <artifactId>amqp-client</artifactId>
+        </dependency>
+
+        <!-- sqlserver  -->
+        <dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>mssql-jdbc</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+
+    </dependencies>
+
+    <!-- 打包 -->
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <!-- 跳过单元测试 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>true</skipTests>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

二進制
eladmin-system/src/.DS_Store


二進制
eladmin-system/src/main/.DS_Store


二進制
eladmin-system/src/main/java/.DS_Store


二進制
eladmin-system/src/main/java/me/.DS_Store


二進制
eladmin-system/src/main/java/me/zhengjie/.DS_Store


+ 82 - 0
eladmin-system/src/main/java/me/zhengjie/AppRun.java

@@ -0,0 +1,82 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie;
+
+import io.swagger.annotations.Api;
+import me.zhengjie.annotation.rest.AnonymousGetMapping;
+import me.zhengjie.utils.SpringContextHolder;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 开启审计功能 -> @EnableJpaAuditing
+ *
+ * @author Zheng Jie
+ * @date 2018/11/15 9:20:19
+ */
+@EnableAsync
+@EnableScheduling
+@RestController
+@Api(hidden = true)
+@SpringBootApplication
+@EnableTransactionManagement
+@EnableJpaAuditing(auditorAwareRef = "auditorAware")
+public class AppRun {
+
+    public static void main(String[] args) {
+        SpringApplication.run(AppRun.class, args);
+    }
+
+    @Bean
+    public SpringContextHolder springContextHolder() {
+        return new SpringContextHolder();
+    }
+
+    @Bean
+    public ServletWebServerFactory webServerFactory() {
+        TomcatServletWebServerFactory fa = new TomcatServletWebServerFactory();
+        fa.addConnectorCustomizers(connector -> connector.setProperty("relaxedQueryChars", "[]{}"));
+        return fa;
+    }
+
+    @Primary
+    @Bean
+    public TaskExecutor primaryTaskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        return executor;
+    }
+
+    /**
+     * 访问首页提示
+     *
+     * @return /
+     */
+    @AnonymousGetMapping("/")
+    public String index() {
+        return "Backend service started successfully";
+    }
+}

+ 88 - 0
eladmin-system/src/main/java/me/zhengjie/config/ConfigurerAdapter.java

@@ -0,0 +1,88 @@
+/*
+ *  Copyright 2019-2020 Zheng Jie
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package me.zhengjie.config;
+
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * WebMvcConfigurer
+ *
+ * @author Zheng Jie
+ * @date 2018-11-30
+ */
+@Configuration
+@EnableWebMvc
+public class ConfigurerAdapter implements WebMvcConfigurer {
+
+    /** 文件配置 */
+    private final FileProperties properties;
+
+    public ConfigurerAdapter(FileProperties properties) {
+        this.properties = properties;
+    }
+
+    @Bean
+    public CorsFilter corsFilter() {
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        CorsConfiguration config = new CorsConfiguration();
+        config.setAllowCredentials(true);
+        config.addAllowedOrigin("*");
+        config.addAllowedHeader("*");
+        config.addAllowedMethod("*");
+        source.registerCorsConfiguration("/**", config);
+        return new CorsFilter(source);
+    }
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        FileProperties.ElPath path = properties.getPath();
+        String avatarUtl = "file:" + path.getAvatar().replace("\\","/");
+        String pathUtl = "file:" + path.getPath().replace("\\","/");
+        registry.addResourceHandler("/avatar/**").addResourceLocations(avatarUtl).setCachePeriod(0);
+        registry.addResourceHandler("/file/**").addResourceLocations(pathUtl).setCachePeriod(0);
+        registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0);
+    }
+
+    @Override
+    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+        // 使用 fastjson 序列化,会导致 @JsonIgnore 失效,可以使用 @JSONField(serialize = false) 替换
+        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
+        List<MediaType> supportMediaTypeList = new ArrayList<>();
+        supportMediaTypeList.add(MediaType.APPLICATION_JSON_UTF8);
+        FastJsonConfig config = new FastJsonConfig();
+        config.setDateFormat("yyyy-MM-dd HH:mm:ss");
+        config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
+        converter.setFastJsonConfig(config);
+        converter.setSupportedMediaTypes(supportMediaTypeList);
+        converter.setDefaultCharset(StandardCharsets.UTF_8);
+        converters.add(converter);
+    }
+}

部分文件因文件數量過多而無法顯示