Bladeren bron

first commit

sunwd 2 maanden geleden
commit
bfc78f5bab
100 gewijzigde bestanden met toevoegingen van 11548 en 0 verwijderingen
  1. 17 0
      .gitignore
  2. 216 0
      LICENSE
  3. 201 0
      jeecg-boot-base-core/pom.xml
  4. 117 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/CommonAPI.java
  5. 38 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/DataLogDTO.java
  6. 31 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileDownDTO.java
  7. 56 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileUploadDTO.java
  8. 58 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/LogDto.java
  9. 49 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/OnlineAuthDTO.java
  10. 28 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/BusinessMessageDto.java
  11. 78 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/MessageDto.java
  12. 31 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/serializer/StringToArraySerializer.java
  13. 4 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/validate/AddGroup.java
  14. 4 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/validate/EditGroup.java
  15. 177 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/api/vo/Result.java
  16. 163 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java
  17. 474 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/DictAspect.java
  18. 139 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/PermissionDataAspect.java
  19. 68 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/UrlMatchEnum.java
  20. 23 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoDict.java
  21. 47 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoLog.java
  22. 54 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/Dict.java
  23. 32 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/DictFillIn.java
  24. 20 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/DynamicTable.java
  25. 19 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/OnlineAuth.java
  26. 25 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/PermissionData.java
  27. 327 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java
  28. 48 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonSendStatus.java
  29. 159 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DataBaseConstant.java
  30. 25 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/FillRuleConstant.java
  31. 54 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ServiceNameConstants.java
  32. 123 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/SymbolConstant.java
  33. 71 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WebsocketConst.java
  34. 167 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/CgformEnum.java
  35. 81 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/DySmsEnum.java
  36. 66 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/EmailTemplateEnum.java
  37. 76 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/FileTypeEnum.java
  38. 84 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/MessageTypeEnum.java
  39. 25 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/ModuleType.java
  40. 99 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/OperateTypeEnum.java
  41. 97 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/RoleIndexConfigEnum.java
  42. 82 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/SysAnnmentTypeEnum.java
  43. 75 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/Vue3MessageHrefEnum.java
  44. 20 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveDecode.java
  45. 20 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveEncode.java
  46. 21 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveField.java
  47. 81 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/aspect/SensitiveDataAspect.java
  48. 55 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/enums/SensitiveEnum.java
  49. 363 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/util/SensitiveInfoUtil.java
  50. 37 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/BisonException.java
  51. 37 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/BisonU8Exception.java
  52. 23 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBoot401Exception.java
  53. 39 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootException.java
  54. 218 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java
  55. 23 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgSqlInjectionException.java
  56. 22 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/handler/IFillRuleHandler.java
  57. 19 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/annotation/EnumDict.java
  58. 55 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java
  59. 61 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/entity/JeecgEntity.java
  60. 12 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/JeecgService.java
  61. 18 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/impl/JeecgServiceImpl.java
  62. 45 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/MatchTypeEnum.java
  63. 81 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryCondition.java
  64. 931 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java
  65. 102 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryRuleEnum.java
  66. 106 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JeecgDataAutorUtils.java
  67. 257 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java
  68. 117 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/ResourceUtil.java
  69. 244 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/SqlConcatUtil.java
  70. 40 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/ComboModel.java
  71. 70 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModel.java
  72. 19 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModelMany.java
  73. 35 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictQuery.java
  74. 58 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DynamicDataSourceModel.java
  75. 131 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java
  76. 32 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SelectTreeModel.java
  77. 50 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysCategoryModel.java
  78. 148 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysDepartModel.java
  79. 144 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysPermissionDataRuleModel.java
  80. 71 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysUserCacheInfo.java
  81. 57 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/UserAccountInfo.java
  82. 470 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java
  83. 762 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java
  84. 126 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DySmsHelper.java
  85. 57 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FillRuleUtil.java
  86. 97 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ImportExcelUtil.java
  87. 59 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/IpUtils.java
  88. 47 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Md5Util.java
  89. 219 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MinioUtil.java
  90. 189 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PasswordUtil.java
  91. 65 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PmsUtil.java
  92. 339 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ReflectHelper.java
  93. 263 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RestUtil.java
  94. 69 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ScheduledThreadPoolUtil.java
  95. 113 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SpringContextUtils.java
  96. 421 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java
  97. 168 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java
  98. 96 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/UUIDGenerator.java
  99. 176 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/YouBianCodeUtil.java
  100. 122 0
      jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/DbTypeUtils.java

+ 17 - 0
.gitignore

@@ -0,0 +1,17 @@
+## ide
+**/.idea
+*.iml
+rebel.xml
+
+## backend
+**/target
+**/logs
+**/nbcb-czsdk
+**/temp
+
+## front
+**/*.lock
+os_del.cmd
+os_del_doc.cmd
+.svn
+derby.log

+ 216 - 0
LICENSE

@@ -0,0 +1,216 @@
+    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:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) 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
+
+      (d) 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 (c) 2019 <a href="http://www.jeecg.com">Jeecg Boot</a> All rights reserved.
+ 
+   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.
+   
+   In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software.
+
+  开源协议补充
+    JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京,地址:中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱:jeecgos@163.com
+    本软件受适用的国家软件著作权法(包括国际条约)和双重保护许可。
+  
+   1.允许基于本平台软件开展业务系统开发。
+   2.JeecgBoot底层依赖的非开源功能:online lib依赖、仪表盘lib依赖等,统一采用LGPL开源协议(不二次改造、不拆分出jeecgboot之外使用,就不产生侵权)
+   3.不得基于该平台软件的基础,修改包装成一个与JeecgBoot平台软件功能类似的产品进行发布、销售,或与JeecgBoot参与同类软件产品市场的竞争。
+	 违反此条款属于侵权行为,须赔偿侵权经济损失,同时立即停止著作权侵权行为。
+	 
+     总结:在遵循Apache开源协议和开源协议补充条款下,允许商用使用,不会造成侵权行为!
+	 解释权归:http://www.jeecg.com
+	 

+ 201 - 0
jeecg-boot-base-core/pom.xml

@@ -0,0 +1,201 @@
+<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>
+		<groupId>org.jeecgframework.boot</groupId>
+		<artifactId>jeecg-boot-parent</artifactId>
+		<version>3.6.3</version>
+	</parent>
+	<modelVersion>4.0.0</modelVersion>
+	<artifactId>jeecg-boot-base-core</artifactId>
+
+	<repositories>
+		<repository>
+			<id>aliyun</id>
+			<name>aliyun Repository</name>
+			<url>https://maven.aliyun.com/repository/public</url>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+		<repository>
+			<id>jeecg</id>
+			<name>jeecg Repository</name>
+			<url>https://maven.jeecg.org/nexus/content/repositories/jeecg</url>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+		<repository>
+			<id>jeecg-snapshots</id>
+			<name>jeecg-snapshots Repository</name>
+			<url>https://oss.sonatype.org/content/repositories/snapshots</url>
+			<releases>
+				<enabled>false</enabled>
+			</releases>
+			<snapshots>
+				<enabled>true</enabled>
+			</snapshots>
+		</repository>
+	</repositories>
+
+	<dependencies>
+		<!--jeecg-tools-->
+		<dependency>
+			<groupId>org.jeecgframework.boot</groupId>
+			<artifactId>jeecg-boot-common</artifactId>
+		</dependency>
+		<!--集成springmvc框架并实现自动配置 -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+		<!-- websocket -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-websocket</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-mail</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-aop</artifactId>
+		</dependency>
+		<!--springboot2.3+ 需引入validation对应的包-->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-validation</artifactId>
+		</dependency>
+
+		<!-- commons -->
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>${commons-io.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-lang</groupId>
+			<artifactId>commons-lang</artifactId>
+			<version>${commons.version}</version>
+		</dependency>
+		<!-- freemarker -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-freemarker</artifactId>
+		</dependency>
+
+		<!-- mybatis-plus -->
+		<dependency>
+			<groupId>com.baomidou</groupId>
+			<artifactId>mybatis-plus-boot-starter</artifactId>
+			<version>${mybatis-plus.version}</version>
+		</dependency>
+
+		<!-- 动态数据源 -->
+		<dependency>
+			<groupId>com.baomidou</groupId>
+			<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+			<version>${dynamic-datasource-spring-boot-starter.version}</version>
+		</dependency>
+
+		<!-- 数据库驱动 -->
+		<!--mysql-->
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<version>${mysql-connector-java.version}</version>
+			<scope>runtime</scope>
+		</dependency>
+		<!--JWT-->
+		<dependency>
+			<groupId>com.auth0</groupId>
+			<artifactId>java-jwt</artifactId>
+			<version>${java-jwt.version}</version>
+		</dependency>
+
+		<!--shiro-->
+		<dependency>
+			<groupId>org.apache.shiro</groupId>
+			<artifactId>shiro-spring-boot-starter</artifactId>
+			<version>${shiro.version}</version>
+		</dependency>
+		<!-- shiro-redis -->
+		<dependency>
+			<groupId>org.crazycake</groupId>
+			<artifactId>shiro-redis</artifactId>
+			<version>${shiro-redis.version}</version>
+			<exclusions>
+				<exclusion>
+					<groupId>org.apache.shiro</groupId>
+					<artifactId>shiro-core</artifactId>
+				</exclusion>
+				<exclusion>
+					<artifactId>checkstyle</artifactId>
+					<groupId>com.puppycrawl.tools</groupId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+
+		<!-- knife4j -->
+<!--		<dependency>
+			<groupId>com.github.xiaoymin</groupId>
+			<artifactId>knife4j-spring-boot-starter</artifactId>
+			<version>3.0.3</version>
+		</dependency>-->
+		<dependency>
+			<groupId>com.github.xiaoymin</groupId>
+			<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
+			<version>${knife4j-spring-boot-starter.version}</version>
+		</dependency>
+
+		<!-- mini文件存储服务 -->
+		<dependency>
+			<groupId>io.minio</groupId>
+			<artifactId>minio</artifactId>
+		</dependency>
+
+		<!-- 阿里云短信 -->
+		<dependency>
+			<groupId>com.aliyun</groupId>
+			<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
+			<version>${aliyun-java-sdk-dysmsapi.version}</version>
+		</dependency>
+		<!-- aliyun oss -->
+		<dependency>
+			<groupId>com.aliyun.oss</groupId>
+			<artifactId>aliyun-sdk-oss</artifactId>
+			<version>${aliyun.oss.version}</version>
+		</dependency>
+		<!-- 第三方登录  -->
+		<dependency>
+			<groupId>com.xkcoding.justauth</groupId>
+			<artifactId>justauth-spring-boot-starter</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.squareup.okhttp3</groupId>
+			<artifactId>okhttp</artifactId>
+		</dependency>
+		<!-- 解决okhttp引用了kotlin,应用启动有警告日志问题 -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.module</groupId>
+			<artifactId>jackson-module-kotlin</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-fileupload</groupId>
+			<artifactId>commons-fileupload</artifactId>
+		</dependency>
+		<!--加载hutool-->
+		<dependency>
+			<groupId>cn.hutool</groupId>
+			<artifactId>hutool-all</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>cn.hutool</groupId>
+			<artifactId>hutool-crypto</artifactId>
+		</dependency>
+
+	</dependencies>
+
+</project>

+ 117 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/CommonAPI.java

@@ -0,0 +1,117 @@
+package org.jeecg.common.api;
+
+import org.jeecg.common.system.vo.*;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 通用api
+ * @author: jeecg-boot
+ */
+public interface CommonAPI {
+
+    /**
+     * 1查询用户角色信息
+     * @param username
+     * @return
+     */
+    Set<String> queryUserRoles(String username);
+
+
+    /**
+     * 2查询用户权限信息
+     * @param userId
+     * @return
+     */
+    Set<String> queryUserAuths(String userId);
+
+    /**
+     * 5根据用户账号查询用户信息
+     * @param username
+     * @return
+     */
+    public LoginUser getUserByName(String username);
+
+
+    /**
+     * 6字典表的 翻译
+     * @param table
+     * @param text
+     * @param code
+     * @param key
+     * @return
+     */
+    String translateDictFromTable(String table, String text, String code, String key);
+
+    /**
+     * 7普通字典的翻译
+     * @param code
+     * @param key
+     * @return
+     */
+    String translateDict(String code, String key);
+
+    /**
+     * 8查询数据权限
+     * @param component 组件
+     * @param username 用户名
+     * @param requestPath 前段请求地址
+     * @return
+     */
+    List<SysPermissionDataRuleModel> queryPermissionDataRule(String component, String requestPath, String username);
+
+
+    /**
+     * 9查询用户信息
+     * @param username
+     * @return
+     */
+    SysUserCacheInfo getCacheUser(String username);
+
+    /**
+     * 10获取数据字典
+     * @param code
+     * @return
+     */
+    public List<DictModel> queryDictItemsByCode(String code);
+
+    /**
+     * 获取有效的数据字典项
+     * @param code
+     * @return
+     */
+    public List<DictModel> queryEnableDictItemsByCode(String code);
+
+    /**
+     * 13获取表数据字典
+     * @param tableFilterSql
+     * @param text
+     * @param code
+     * @return
+     */
+    List<DictModel> queryTableDictItemsByCode(String tableFilterSql, String text, String code);
+
+    /**
+     * 14 普通字典的翻译,根据多个dictCode和多条数据,多个以逗号分割
+     * @param dictCodes 例如:user_status,sex
+     * @param keys 例如:1,2,0
+     * @return
+     */
+    Map<String, List<DictModel>> translateManyDict(String dictCodes, String keys);
+
+    //update-begin---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+    /**
+     * 15 字典表的 翻译,可批量
+     * @param table
+     * @param text
+     * @param code
+     * @param keys 多个用逗号分割
+     * @param dataSource 数据源
+     * @return
+     */
+    List<DictModel> translateDictFromTableByKeys(String table, String text, String code, String keys, String dataSource);
+    //update-end---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+
+}

+ 38 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/DataLogDTO.java

@@ -0,0 +1,38 @@
+package org.jeecg.common.api.dto;
+
+import lombok.Data;
+
+/**
+ * @Author taoYan
+ * @Date 2022/7/26 14:44
+ **/
+@Data
+public class DataLogDTO {
+
+    private String tableName;
+
+    private String dataId;
+
+    private String content;
+
+    private String type;
+
+    private String createName;
+
+    public DataLogDTO(){
+
+    }
+
+    public DataLogDTO(String tableName, String dataId, String content, String type) {
+        this.tableName = tableName;
+        this.dataId = dataId;
+        this.content = content;
+        this.type = type;
+    }
+
+    public DataLogDTO(String tableName, String dataId, String type) {
+        this.tableName = tableName;
+        this.dataId = dataId;
+        this.type = type;
+    }
+}

+ 31 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileDownDTO.java

@@ -0,0 +1,31 @@
+package org.jeecg.common.api.dto;
+
+import lombok.Data;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.Serializable;
+
+/**
+ * 文件下载
+ * cloud api 用到的接口传输对象
+ * @author: jeecg-boot
+ */
+@Data
+public class FileDownDTO implements Serializable {
+
+    private static final long serialVersionUID = 6749126258686446019L;
+
+    private String filePath;
+    private String uploadpath;
+    private String uploadType;
+    private HttpServletResponse response;
+
+    public FileDownDTO(){}
+
+    public FileDownDTO(String filePath, String uploadpath, String uploadType,HttpServletResponse response){
+        this.filePath = filePath;
+        this.uploadpath = uploadpath;
+        this.uploadType = uploadType;
+        this.response = response;
+    }
+}

+ 56 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileUploadDTO.java

@@ -0,0 +1,56 @@
+package org.jeecg.common.api.dto;
+
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.Serializable;
+
+/**
+ * 文件上传
+ * cloud api 用到的接口传输对象
+ * @author: jeecg-boot
+ */
+@Data
+public class FileUploadDTO implements Serializable {
+
+    private static final long serialVersionUID = -4111953058578954386L;
+
+    private MultipartFile file;
+
+    private String bizPath;
+
+    private String uploadType;
+
+    private String customBucket;
+
+    public FileUploadDTO(){
+
+    }
+
+    /**
+     * 简单上传 构造器1
+     * @param file
+     * @param bizPath
+     * @param uploadType
+     */
+    public FileUploadDTO(MultipartFile file,String bizPath,String uploadType){
+        this.file = file;
+        this.bizPath = bizPath;
+        this.uploadType = uploadType;
+    }
+
+    /**
+     * 申明桶 文件上传 构造器2
+     * @param file
+     * @param bizPath
+     * @param uploadType
+     * @param customBucket
+     */
+    public FileUploadDTO(MultipartFile file,String bizPath,String uploadType,String customBucket){
+        this.file = file;
+        this.bizPath = bizPath;
+        this.uploadType = uploadType;
+        this.customBucket = customBucket;
+    }
+
+}

+ 58 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/LogDto.java

@@ -0,0 +1,58 @@
+package org.jeecg.common.api.dto;
+
+import lombok.Data;
+import org.jeecg.common.system.vo.LoginUser;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 日志对象
+ * cloud api 用到的接口传输对象
+ *
+ * @author: jeecg-boot
+ */
+@Data
+public class LogDto implements Serializable {
+
+    private static final long serialVersionUID = 8482720462943906924L;
+
+    /** 内容 */
+    private String logContent;
+
+    /** 日志类型(0:操作日志;1:登录日志;2:定时任务) */
+    private Integer logType;
+
+    /** 操作类型(1:添加;2:修改;3:删除;) */
+    private Integer operateType;
+
+    /** 登录用户 */
+    private LoginUser loginUser;
+
+    private String id;
+    private String createBy;
+    private Date createTime;
+    private Long costTime;
+    private String ip;
+
+    /** 请求参数 */
+    private String requestParam;
+
+    /** 请求类型 */
+    private String requestType;
+
+    /** 请求路径 */
+    private String requestUrl;
+
+    /** 请求方法 */
+    private String method;
+
+    /** 操作人用户名称 */
+    private String username;
+
+    /** 操作人用户账户 */
+    private String userid;
+
+    /** 异常信息 */
+    private String exceptions;
+}

+ 49 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/OnlineAuthDTO.java

@@ -0,0 +1,49 @@
+package org.jeecg.common.api.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * online 拦截器权限判断
+ * cloud api 用到的接口传输对象
+ * @author: jeecg-boot
+ */
+@Data
+public class OnlineAuthDTO implements Serializable {
+    private static final long serialVersionUID = 1771827545416418203L;
+
+
+    /**
+     * 用户名
+     */
+    private String username;
+
+    /**
+     * 可能的请求地址
+     */
+    private List<String> possibleUrl;
+
+    /**
+     * online开发的菜单地址
+     */
+    private String onlineFormUrl;
+
+    //update-begin---author:chenrui ---date:20240123  for:[QQYUN-7992]【online】工单申请下的online表单,未配置online表单开发菜单,操作报错无权限------------
+    /**
+     * online工单的地址
+     */
+    private String onlineWorkOrderUrl;
+    //update-end---author:chenrui ---date:20240123  for:[QQYUN-7992]【online】工单申请下的online表单,未配置online表单开发菜单,操作报错无权限------------
+
+    public OnlineAuthDTO(){
+
+    }
+
+    public OnlineAuthDTO(String username, List<String> possibleUrl, String onlineFormUrl){
+        this.username = username;
+        this.possibleUrl = possibleUrl;
+        this.onlineFormUrl = onlineFormUrl;
+    }
+}

+ 28 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/BusinessMessageDto.java

@@ -0,0 +1,28 @@
+package org.jeecg.common.api.dto.message;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+
+/**
+ * 带业务参数的消息
+ *
+ * @author: taoyan
+ * @date: 2022/8/17
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class BusinessMessageDto extends MessageDto implements Serializable {
+
+    private static final long serialVersionUID = 9104793287983367669L;
+    /**
+     * 业务类型
+     */
+    private String busType;
+
+    /**
+     * 业务id
+     */
+    private String busId;
+}

+ 78 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/MessageDto.java

@@ -0,0 +1,78 @@
+package org.jeecg.common.api.dto.message;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * 消息
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class MessageDto implements Serializable {
+    private static final long serialVersionUID = -5690444483968058442L;
+
+    /**
+     * 发送人(用户登录账户)
+     */
+    protected String fromUser;
+
+    /**
+     * 发送给(用户登录账户)
+     */
+    protected String toUser;
+
+    /**
+     * 发送给所有人
+     */
+    protected Boolean toAll;
+
+    /**
+     * 消息主题
+     */
+    protected String title;
+
+    /**
+     * 消息内容
+     */
+    protected String content;
+
+    /**
+     * 消息类型 1:消息  2:系统消息
+     */
+    protected String category;
+
+    /**
+     * 消息类型:org.jeecg.common.constant.enums.MessageTypeEnum
+     * XT("system",  "系统消息")
+     * YJ("email",  "邮件消息")
+     * DD("dingtalk", "钉钉消息")
+     */
+    protected String type;
+
+    /**
+     * 是否发送Markdown格式的消息
+     */
+    protected boolean isMarkdown;
+
+    /**
+     * 解析模板内容 对应的数据
+     */
+    protected Map<String, Object> data;
+
+    /**
+     * 简单的markdown消息
+     */
+    public MessageDto(String toUser, String title, String content, boolean isMarkdown) {
+        this.toAll = false;
+        this.category = "1";
+        this.toUser = toUser;
+        this.title = title;
+        this.content = content;
+        this.isMarkdown = isMarkdown;
+    }
+}

+ 31 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/serializer/StringToArraySerializer.java

@@ -0,0 +1,31 @@
+package org.jeecg.common.api.serializer;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.IOException;
+
+@NoArgsConstructor
+public class StringToArraySerializer extends JsonSerializer<String> {
+
+    @Override
+    public void serialize(String jsonStr, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+        if (StringUtils.isNotBlank(jsonStr)) {
+            try {
+                JSONArray jsonArray = JSONArray.parseArray(jsonStr);
+                String[] strArray = new String[jsonArray.size()];
+                for (int i = 0; i < jsonArray.size(); i++) {
+                    strArray[i] = jsonArray.getString(i);
+                }
+                jsonGenerator.writeArray(strArray, 0, strArray.length);
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}

+ 4 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/validate/AddGroup.java

@@ -0,0 +1,4 @@
+package org.jeecg.common.api.validate;
+
+public interface AddGroup {
+}

+ 4 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/validate/EditGroup.java

@@ -0,0 +1,4 @@
+package org.jeecg.common.api.validate;
+
+public interface EditGroup {
+}

+ 177 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/api/vo/Result.java

@@ -0,0 +1,177 @@
+package org.jeecg.common.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.jeecg.common.constant.CommonConstant;
+
+import java.io.Serializable;
+
+/**
+ *   接口返回数据格式
+ * @author scott
+ * @email jeecgos@163.com
+ * @date  2019年1月19日
+ */
+@Data
+@ApiModel(value="接口返回对象", description="接口返回对象")
+public class Result<T> implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 成功标志
+	 */
+	@ApiModelProperty(value = "成功标志")
+	private boolean success = true;
+
+	/**
+	 * 返回处理消息
+	 */
+	@ApiModelProperty(value = "返回处理消息")
+	private String message = "";
+
+	/**
+	 * 返回代码
+	 */
+	@ApiModelProperty(value = "返回代码")
+	private Integer code = 0;
+	
+	/**
+	 * 返回数据对象 data
+	 */
+	@ApiModelProperty(value = "返回数据对象")
+	private T result;
+	
+	/**
+	 * 时间戳
+	 */
+	@ApiModelProperty(value = "时间戳")
+	private long timestamp = System.currentTimeMillis();
+
+	public Result() {
+	}
+
+    /**
+     * 兼容VUE3版token失效不跳转登录页面
+     * @param code
+     * @param message
+     */
+	public Result(Integer code, String message) {
+		this.code = code;
+		this.message = message;
+	}
+	
+	public Result<T> success(String message) {
+		this.message = message;
+		this.code = CommonConstant.SC_OK_200;
+		this.success = true;
+		return this;
+	}
+
+	public static<T> Result<T> ok() {
+		Result<T> r = new Result<T>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		return r;
+	}
+
+	public static<T> Result<T> ok(String msg) {
+		Result<T> r = new Result<T>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		//Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D
+		r.setResult((T) msg);
+		r.setMessage(msg);
+		return r;
+	}
+
+	public static<T> Result<T> ok(T data) {
+		Result<T> r = new Result<T>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		r.setResult(data);
+		return r;
+	}
+
+	public static<T> Result<T> OK() {
+		Result<T> r = new Result<T>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		return r;
+	}
+
+	/**
+	 * 此方法是为了兼容升级所创建
+	 *
+	 * @param msg
+	 * @param <T>
+	 * @return
+	 */
+	public static<T> Result<T> OK(String msg) {
+		Result<T> r = new Result<T>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		r.setMessage(msg);
+		//Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D
+		r.setResult((T) msg);
+		return r;
+	}
+
+	public static<T> Result<T> OK(T data) {
+		Result<T> r = new Result<T>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		r.setResult(data);
+		return r;
+	}
+
+	public static<T> Result<T> OK(String msg, T data) {
+		Result<T> r = new Result<T>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		r.setMessage(msg);
+		r.setResult(data);
+		return r;
+	}
+
+	public static<T> Result<T> error(String msg, T data) {
+		Result<T> r = new Result<T>();
+		r.setSuccess(false);
+		r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
+		r.setMessage(msg);
+		r.setResult(data);
+		return r;
+	}
+
+	public static<T> Result<T> error(String msg) {
+		return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
+	}
+	
+	public static<T> Result<T> error(int code, String msg) {
+		Result<T> r = new Result<T>();
+		r.setCode(code);
+		r.setMessage(msg);
+		r.setSuccess(false);
+		return r;
+	}
+
+	public Result<T> error500(String message) {
+		this.message = message;
+		this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
+		this.success = false;
+		return this;
+	}
+
+	/**
+	 * 无权限访问返回结果
+	 */
+	public static<T> Result<T> noauth(String msg) {
+		return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg);
+	}
+
+	@JsonIgnore
+	private String onlTable;
+
+}

+ 163 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java

@@ -0,0 +1,163 @@
+package org.jeecg.common.aspect;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.PropertyFilter;
+import org.apache.shiro.SecurityUtils;
+import org.aspectj.lang.JoinPoint;
+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.jeecg.common.api.dto.LogDto;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.enums.ModuleType;
+import org.jeecg.common.constant.enums.OperateTypeEnum;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.IpUtils;
+import org.jeecg.common.util.SpringContextUtils;
+import org.jeecg.common.util.oConvertUtils;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+import java.util.Date;
+
+/**
+ * 系统日志,切面处理类
+ */
+@Aspect
+@Component
+public class AutoLogAspect {
+
+    @Pointcut("@annotation(org.jeecg.common.aspect.annotation.AutoLog)")
+    public void logPointCut() {
+    }
+
+    @Around("logPointCut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        long beginTime = System.currentTimeMillis();
+        try {
+            Object result = point.proceed();
+            long time = System.currentTimeMillis() - beginTime;
+            LogDto logDto = getLogDto(point, time, result, null);
+            SpringUtil.getApplicationContext().publishEvent(logDto);
+            return result;
+        } catch (Exception e) {
+            long time = System.currentTimeMillis() - beginTime;
+            LogDto logDto = getLogDto(point, time, null, e.getMessage());
+            SpringUtil.getApplicationContext().publishEvent(logDto);
+            throw e;
+        }
+    }
+
+    private LogDto getLogDto(JoinPoint joinPoint, Long time, Object obj, String exceptionMsg) {
+        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+        String methodName = signature.getName();
+        String className = joinPoint.getTarget().getClass().getName();
+
+        AutoLog syslog = method.getAnnotation(AutoLog.class);
+        String content = syslog.value();
+        ModuleType module = syslog.module();
+        int logType = syslog.logType();
+        int operateType = syslog.operateType();
+        if (module == ModuleType.ONLINE && obj != null) {
+            content = getOnlineLogContent(obj, content);
+        }
+
+        LogDto dto = new LogDto();
+        dto.setLogType(logType);
+        dto.setLogContent(content);
+        dto.setCostTime(time);
+        dto.setCreateTime(new Date());
+        dto.setExceptions(exceptionMsg);
+        dto.setMethod(className + "." + methodName);
+        dto.setIp(IpUtils.getIpAddr(request));
+        dto.setRequestType(request.getMethod());
+        dto.setRequestUrl(request.getRequestURI());
+        if (module != ModuleType.EMAIL) // 不保存邮件的参数
+            dto.setRequestParam(getReqestParams(request, joinPoint));
+        if (CommonConstant.LOG_TYPE_2 == dto.getLogType()) // 是操作类的日志
+            dto.setOperateType(getOperateType(methodName, operateType));
+        if (loginUser != null) {
+            dto.setLoginUser(loginUser);
+            dto.setUserid(loginUser.getUsername());
+            dto.setUsername(loginUser.getRealname());
+            dto.setCreateBy(loginUser.getRealname());
+        }
+        return dto;
+    }
+
+    /**
+     * 获取操作类型
+     */
+    private int getOperateType(String methodName, int operateType) {
+        if (operateType > 0) {
+            return operateType;
+        }
+        return OperateTypeEnum.getTypeByMethodName(methodName);
+    }
+
+    /**
+     * 获取请求参数
+     */
+    private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) {
+        String httpMethod = request.getMethod();
+        String params = "";
+        if (CommonConstant.HTTP_POST.equals(httpMethod) || CommonConstant.HTTP_PUT.equals(httpMethod) || CommonConstant.HTTP_PATCH.equals(httpMethod)) {
+            Object[] paramsArray = joinPoint.getArgs();
+            Object[] arguments = new Object[paramsArray.length];
+            for (int i = 0; i < paramsArray.length; i++) {
+                if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
+                    continue;
+                }
+                arguments[i] = paramsArray[i];
+            }
+            PropertyFilter profilter = (o, name, value) -> true;
+            params = JSONObject.toJSONString(arguments, profilter);
+        } else {
+            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+            Method method = signature.getMethod();
+            Object[] args = joinPoint.getArgs();
+            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
+            String[] paramNames = u.getParameterNames(method);
+            if (args != null && paramNames != null) {
+                for (int i = 0; i < args.length; i++) {
+                    params += "  " + paramNames[i] + ": " + args[i];
+                }
+            }
+        }
+        return params;
+    }
+
+    /**
+     * online日志内容拼接
+     */
+    private String getOnlineLogContent(Object obj, String content) {
+        if (obj instanceof Result) {
+            Result res = (Result) obj;
+            String msg = res.getMessage();
+            String tableName = res.getOnlTable();
+            if (oConvertUtils.isNotEmpty(tableName)) {
+                content += ",表名:" + tableName;
+            }
+            if (res.isSuccess()) {
+                content += "," + (oConvertUtils.isEmpty(msg) ? "操作成功" : msg);
+            } else {
+                content += "," + (oConvertUtils.isEmpty(msg) ? "操作失败" : msg);
+            }
+        }
+        return content;
+    }
+}

+ 474 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/DictAspect.java

@@ -0,0 +1,474 @@
+package org.jeecg.common.aspect;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.parser.Feature;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+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.jeecg.common.api.CommonAPI;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.Dict;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.system.vo.DictModel;
+import org.jeecg.common.util.oConvertUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 字典aop类
+ * @Author: dangzhenghui
+ * @Date: 2019-3-17 21:50
+ * @Version: 1.0
+ */
+@Aspect
+@Component
+@Slf4j
+public class DictAspect {
+    @Lazy
+    @Autowired
+    private CommonAPI commonApi;
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    private static final String JAVA_UTIL_DATE = "java.util.Date";
+
+    /**
+     * 定义切点Pointcut
+     */
+    @Pointcut("execution(public * org.jeecg.modules..*.*Controller.*(..)) || @annotation(org.jeecg.common.aspect.annotation.AutoDict)")
+    public void excudeService() {
+    }
+
+    @Around("excudeService()")
+    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
+        long time1 = System.currentTimeMillis();
+        Object result = pjp.proceed();
+        long time2 = System.currentTimeMillis();
+        log.debug("获取JSON数据 耗时:{}ms", time2 - time1);
+        long start = System.currentTimeMillis();
+        result = this.parseDictText(result);
+        long end = System.currentTimeMillis();
+        log.debug("注入字典到JSON数据  耗时{}ms", end - start);
+        return result;
+    }
+
+    /**
+     * 本方法针对返回对象为Result 的IPage的分页列表数据进行动态字典注入
+     * 字典注入实现 通过对实体类添加注解@dict 来标识需要的字典内容,字典分为单字典code即可 ,table字典 code table text配合使用与原来jeecg的用法相同
+     * 示例为SysUser   字段为sex 添加了注解@Dict(dicCode = "sex") 会在字典服务立马查出来对应的text 然后在请求list的时候将这个字典text,已字段名称加_dictText形式返回到前端
+     * 例输入当前返回值的就会多出一个sex_dictText字段
+     * {
+     * sex:1,
+     * sex_dictText:"男"
+     * }
+     * 前端直接取值sext_dictText在table里面无需再进行前端的字典转换了
+     * customRender:function (text) {
+     * if(text==1){
+     * return "男";
+     * }else if(text==2){
+     * return "女";
+     * }else{
+     * return text;
+     * }
+     * }
+     * 目前vue是这么进行字典渲染到table上的多了就很麻烦了 这个直接在服务端渲染完成前端可以直接用
+     *
+     * @param result
+     */
+    private Object parseDictText(Object result) {
+        if (result instanceof Result) {
+            if (((Result) result).getResult() instanceof IPage || ((Result) result).getResult() instanceof List) {
+                List<JSONObject> items = new ArrayList<>();
+
+                // step.1 筛选出加了 Dict 注解的字段列表
+                List<Field> dictFieldList = new ArrayList<>();
+                // 字典数据列表, key = 字典code,value=数据列表
+                Map<String, List<String>> dataListMap = new HashMap<>();
+                // 取出结果集
+                List<Object> records = Collections.emptyList();
+                if (((Result) result).getResult() instanceof IPage) {
+                    records = ((IPage) ((Result) result).getResult()).getRecords();
+                } else if (((Result) result).getResult() instanceof List) {
+                    records = ((List) ((Result) result).getResult());
+                }
+                // update-begin--Author:zyf -- Date:20220606 ----for:【VUEN-1230】 判断是否含有字典注解,没有注解返回-----
+                Boolean hasDict = checkHasDict(records);
+                if (!hasDict) {
+                    return result;
+                }
+
+                log.debug(" __ 进入字典翻译切面 DictAspect —— ");
+                // update-end--Author:zyf -- Date:20220606 ----for:【VUEN-1230】 判断是否含有字典注解,没有注解返回-----
+                for (Object record : records) {
+                    String json = "{}";
+                    try {
+                        // update-begin--Author:zyf -- Date:20220531 ----for:【issues/#3629】 DictAspect Jackson序列化报错-----
+                        // 解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
+                        json = objectMapper.writeValueAsString(record);
+                        // update-end--Author:zyf -- Date:20220531 ----for:【issues/#3629】 DictAspect Jackson序列化报错-----
+                    } catch (JsonProcessingException e) {
+                        log.error("json解析失败" + e.getMessage(), e);
+                    }
+                    // update-begin--Author:scott -- Date:20211223 ----for:【issues/3303】restcontroller返回json数据后key顺序错乱 -----
+                    JSONObject item = JSONObject.parseObject(json, Feature.OrderedField);
+                    // update-end--Author:scott -- Date:20211223 ----for:【issues/3303】restcontroller返回json数据后key顺序错乱 -----
+
+                    // update-begin--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
+                    // for (Field field : record.getClass().getDeclaredFields()) {
+                    // 遍历所有字段,把字典Code取出来,放到 map 里
+                    for (Field field : oConvertUtils.getAllFields(record)) {
+                        String value = item.getString(field.getName());
+                        if (oConvertUtils.isEmpty(value)) {
+                            continue;
+                        }
+                        // update-end--Author:scott  -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
+                        if (field.getAnnotation(Dict.class) != null) {
+                            if (!dictFieldList.contains(field)) {
+                                dictFieldList.add(field);
+                            }
+                            String code = field.getAnnotation(Dict.class).dicCode();
+                            String text = field.getAnnotation(Dict.class).dicText();
+                            String table = field.getAnnotation(Dict.class).dictTable();
+                            // update-begin---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                            String dataSource = field.getAnnotation(Dict.class).ds();
+                            // update-end---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                            List<String> dataList;
+                            String dictCode = code;
+                            if (!StringUtils.isEmpty(table)) {
+                                // update-begin---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                                dictCode = String.format("%s,%s,%s,%s", table, text, code, dataSource);
+                                // update-end---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                            }
+                            dataList = dataListMap.computeIfAbsent(dictCode, k -> new ArrayList<>());
+                            this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(",")));
+                        }
+                        // date类型默认转换string格式化日期
+                        // update-begin--Author:zyf -- Date:20220531 ----for:【issues/#3629】 DictAspect Jackson序列化报错-----
+                        // if (JAVA_UTIL_DATE.equals(field.getType().getName())&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){
+                        // SimpleDateFormat aDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                        // item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName()))));
+                        //}
+                        // update-end--Author:zyf -- Date:20220531 ----for:【issues/#3629】 DictAspect Jackson序列化报错-----
+                    }
+                    items.add(item);
+                }
+
+                // step.2 调用翻译方法,一次性翻译
+                Map<String, List<DictModel>> translText = this.translateAllDict(dataListMap);
+
+                // step.3 将翻译结果填充到返回结果里
+                for (JSONObject record : items) {
+                    for (Field field : dictFieldList) {
+                        String code = field.getAnnotation(Dict.class).dicCode();
+                        String text = field.getAnnotation(Dict.class).dicText();
+                        String table = field.getAnnotation(Dict.class).dictTable();
+                        // update-begin---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                        // 自定义的字典表数据源
+                        String dataSource = field.getAnnotation(Dict.class).ds();
+                        // update-end---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                        String fieldDictCode = code;
+                        if (!StringUtils.isEmpty(table)) {
+                            // update-begin---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                            fieldDictCode = String.format("%s,%s,%s,%s", table, text, code, dataSource);
+                            // update-end---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                        }
+
+                        String value = record.getString(field.getName());
+                        if (oConvertUtils.isNotEmpty(value)) {
+                            List<DictModel> dictModels = translText.get(fieldDictCode);
+                            if (dictModels == null || dictModels.size() == 0) {
+                                continue;
+                            }
+                            String textValue = this.translDictText(dictModels, value);
+                            // log.debug(" 字典Val : " + textValue);
+                            // log.debug(" 翻译字典字段 " + field.getName() + CommonConstant.DICT_TEXT_SUFFIX + ": " + textValue);
+                            // log.debug(" dictCode={},value={},text={},dictModels={}: ", fieldDictCode, value, textValue, JSON.toJSONString(dictModels));
+                            record.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue);
+                        }
+                    }
+                }
+
+                if (((Result) result).getResult() instanceof IPage) {
+                    ((IPage) ((Result) result).getResult()).setRecords(items);
+                } else if (((Result) result).getResult() instanceof List) {
+                    ((Result) result).setResult(items);
+                }
+//                ((IPage) ((Result) result).getResult()).setRecords(items);
+            }
+
+            if (((Result<?>) result).getResult() != null && ((Result<?>) result).getResult().getClass().getDeclaredAnnotation(TableName.class) != null) {
+                Object obj = ((Result<?>) result).getResult();
+                log.debug(" ---- 实体类进行字典映射");
+                // step.1 筛选出加了 Dict 注解的字段列表
+                List<Field> dictFieldList = new ArrayList<>();
+                // 字典数据列表, key = 字典code,value=数据列表
+                Map<String, List<String>> dataListMap = new HashMap<>();
+                // 取出结果集
+                Boolean hasDict = checkHasDict(Arrays.asList(obj));
+                if (!hasDict) {
+                    return result;
+                }
+                String json = "{}";
+                try {
+                    json = objectMapper.writeValueAsString(obj);
+                } catch (JsonProcessingException e) {
+                    log.error("json解析失败" + e.getMessage(), e);
+                }
+                JSONObject item = JSONObject.parseObject(json, Feature.OrderedField);
+                // 遍历所有字段,把字典Code取出来,放到 map 里
+                for (Field field : oConvertUtils.getAllFields(obj)) {
+                    String value = item.getString(field.getName());
+                    if (oConvertUtils.isEmpty(value)) {
+                        continue;
+                    }
+                    if (field.getAnnotation(Dict.class) != null) {
+                        if (!dictFieldList.contains(field)) {
+                            dictFieldList.add(field);
+                        }
+                        String code = field.getAnnotation(Dict.class).dicCode();
+                        String text = field.getAnnotation(Dict.class).dicText();
+                        String table = field.getAnnotation(Dict.class).dictTable();
+                        String dataSource = field.getAnnotation(Dict.class).ds();
+                        List<String> dataList;
+                        String dictCode = code;
+                        if (!StringUtils.isEmpty(table)) {
+                            dictCode = String.format("%s,%s,%s,%s", table, text, code, dataSource);
+                        }
+                        dataList = dataListMap.computeIfAbsent(dictCode, k -> new ArrayList<>());
+                        this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(",")));
+                    }
+                }
+
+                // step.2 调用翻译方法,一次性翻译
+                Map<String, List<DictModel>> translText = this.translateAllDict(dataListMap);
+
+                // step.3 将翻译结果填充到返回结果里
+                for (Field field : dictFieldList) {
+                    String code = field.getAnnotation(Dict.class).dicCode();
+                    String text = field.getAnnotation(Dict.class).dicText();
+                    String table = field.getAnnotation(Dict.class).dictTable();
+                    // 自定义的字典表数据源
+                    String dataSource = field.getAnnotation(Dict.class).ds();
+                    String fieldDictCode = code;
+                    if (!StringUtils.isEmpty(table)) {
+                        fieldDictCode = String.format("%s,%s,%s,%s", table, text, code, dataSource);
+                    }
+
+                    String value = item.getString(field.getName());
+                    if (oConvertUtils.isNotEmpty(value)) {
+                        List<DictModel> dictModels = translText.get(fieldDictCode);
+                        if (dictModels == null || dictModels.size() == 0) {
+                            continue;
+                        }
+                        String textValue = this.translDictText(dictModels, value);
+                        // log.debug(" 字典Val : " + textValue);
+                        // log.debug(" 翻译字典字段 " + field.getName() + CommonConstant.DICT_TEXT_SUFFIX + ": " + textValue);
+                        // log.debug(" dictCode={},value={},text={},dictModels={}: ", fieldDictCode, value, textValue, JSON.toJSONString(dictModels));
+                        item.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue);
+                    }
+                }
+                ((Result) result).setResult(item);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * list 去重添加
+     */
+    private void listAddAllDeduplicate(List<String> dataList, List<String> addList) {
+        // 筛选出dataList中没有的数据
+        List<String> filterList = addList.stream().filter(i -> !dataList.contains(i)).collect(Collectors.toList());
+        dataList.addAll(filterList);
+    }
+
+    /**
+     * 一次性把所有的字典都翻译了
+     * 1.  所有的普通数据字典的所有数据只执行一次SQL
+     * 2.  表字典相同的所有数据只执行一次SQL
+     *
+     * @param dataListMap
+     * @return
+     */
+    public Map<String, List<DictModel>> translateAllDict(Map<String, List<String>> dataListMap) {
+        // 翻译后的字典文本,key=dictCode
+        Map<String, List<DictModel>> translText = new HashMap<>(5);
+        // 需要翻译的数据(有些可以从redis缓存中获取,就不走数据库查询)
+        List<String> needTranslData = new ArrayList<>();
+        // step.1 先通过redis中获取缓存字典数据
+        for (String dictCode : dataListMap.keySet()) {
+            List<String> dataList = dataListMap.get(dictCode);
+            if (dataList.size() == 0) {
+                continue;
+            }
+            // 表字典需要翻译的数据
+            List<String> needTranslDataTable = new ArrayList<>();
+            for (String s : dataList) {
+                String data = s.trim();
+                if (data.length() == 0) {
+                    continue; // 跳过循环
+                }
+                if (dictCode.contains(",")) {
+                    String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, data);
+                    if (redisTemplate.hasKey(keyString)) {
+                        try {
+                            String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
+                            List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
+                            list.add(new DictModel(data, text));
+                        } catch (Exception e) {
+                            log.warn(e.getMessage());
+                        }
+                    } else if (!needTranslDataTable.contains(data)) {
+                        // 去重添加
+                        needTranslDataTable.add(data);
+                    }
+                } else {
+                    String keyString = String.format("sys:cache:dict::%s:%s", dictCode, data);
+                    if (redisTemplate.hasKey(keyString)) {
+                        try {
+                            String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
+                            List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
+                            list.add(new DictModel(data, text));
+                        } catch (Exception e) {
+                            log.warn(e.getMessage());
+                        }
+                    } else if (!needTranslData.contains(data)) {
+                        // 去重添加
+                        needTranslData.add(data);
+                    }
+                }
+
+            }
+            // step.2 调用数据库翻译表字典
+            if (needTranslDataTable.size() > 0) {
+                String[] arr = dictCode.split(",");
+                String table = arr[0], text = arr[1], code = arr[2];
+                String values = String.join(",", needTranslDataTable);
+                // update-begin---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                // 自定义的数据源
+                String dataSource = null;
+                if (arr.length > 3) {
+                    dataSource = arr[3];
+                }
+                // update-end---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                log.debug("translateDictFromTableByKeys.dictCode:" + dictCode);
+                log.debug("translateDictFromTableByKeys.values:" + values);
+                // update-begin---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+
+                // update-begin---author:wangshuai---date:2024-01-09---for:微服务下为空报错没有参数需要传递空字符串---
+                if (null == dataSource) {
+                    dataSource = "";
+                }
+                // update-end---author:wangshuai---date:2024-01-09---for:微服务下为空报错没有参数需要传递空字符串---
+
+                List<DictModel> texts = commonApi.translateDictFromTableByKeys(table, text, code, values, dataSource);
+                // update-end---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+                log.debug("translateDictFromTableByKeys.result:" + texts);
+                List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
+                list.addAll(texts);
+
+                // 做 redis 缓存
+                for (DictModel dict : texts) {
+                    String redisKey = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, dict.getValue());
+                    try {
+                        // update-begin-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061
+                        // 保留5分钟
+                        redisTemplate.opsForValue().set(redisKey, dict.getText(), 300, TimeUnit.SECONDS);
+                        // update-end-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061
+                    } catch (Exception e) {
+                        log.warn(e.getMessage(), e);
+                    }
+                }
+            }
+        }
+
+        // step.3 调用数据库进行翻译普通字典
+        if (needTranslData.size() > 0) {
+            List<String> dictCodeList = Arrays.asList(dataListMap.keySet().toArray(new String[]{}));
+            // 将不包含逗号的字典code筛选出来,因为带逗号的是表字典,而不是普通的数据字典
+            List<String> filterDictCodes = dictCodeList.stream().filter(key -> !key.contains(",")).collect(Collectors.toList());
+            String dictCodes = String.join(",", filterDictCodes);
+            String values = String.join(",", needTranslData);
+            log.debug("translateManyDict.dictCodes:" + dictCodes);
+            log.debug("translateManyDict.values:" + values);
+            Map<String, List<DictModel>> manyDict = commonApi.translateManyDict(dictCodes, values);
+            log.debug("translateManyDict.result:" + manyDict);
+            for (String dictCode : manyDict.keySet()) {
+                List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
+                List<DictModel> newList = manyDict.get(dictCode);
+                list.addAll(newList);
+
+                // 做 redis 缓存
+                for (DictModel dict : newList) {
+                    String redisKey = String.format("sys:cache:dict::%s:%s", dictCode, dict.getValue());
+                    try {
+                        redisTemplate.opsForValue().set(redisKey, dict.getText());
+                    } catch (Exception e) {
+                        log.warn(e.getMessage(), e);
+                    }
+                }
+            }
+        }
+        return translText;
+    }
+
+    /**
+     * 字典值替换文本
+     *
+     * @param dictModels
+     * @param values
+     * @return
+     */
+    public String translDictText(List<DictModel> dictModels, String values) {
+        List<String> result = new ArrayList<>();
+
+        // 允许多个逗号分隔,允许传数组对象
+        String[] splitVal = values.split(",");
+        for (String val : splitVal) {
+            String dictText = val;
+            for (DictModel dict : dictModels) {
+                if (val.equals(dict.getValue())) {
+                    dictText = dict.getText();
+                    break;
+                }
+            }
+            result.add(dictText);
+        }
+        return String.join(",", result);
+    }
+
+    /**
+     * 检测返回结果集中是否包含Dict注解
+     *
+     * @param records
+     * @return
+     */
+    private Boolean checkHasDict(List<Object> records) {
+        if (oConvertUtils.isNotEmpty(records) && records.size() > 0) {
+            for (Field field : oConvertUtils.getAllFields(records.get(0))) {
+                if (oConvertUtils.isNotEmpty(field.getAnnotation(Dict.class))) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+}

+ 139 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/PermissionDataAspect.java

@@ -0,0 +1,139 @@
+package org.jeecg.common.aspect;
+
+import lombok.extern.slf4j.Slf4j;
+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.jeecg.common.api.CommonAPI;
+import org.jeecg.common.aspect.annotation.PermissionData;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.SymbolConstant;
+import org.jeecg.common.system.query.QueryRuleEnum;
+import org.jeecg.common.system.util.JeecgDataAutorUtils;
+import org.jeecg.common.system.util.JwtUtil;
+import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
+import org.jeecg.common.system.vo.SysUserCacheInfo;
+import org.jeecg.common.util.SpringContextUtils;
+import org.jeecg.common.util.oConvertUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * 数据权限切面处理类
+ *  当被请求的方法有注解PermissionData时,会在往当前request中写入数据权限信息
+ * @Date 2019年4月10日
+ * @Version: 1.0
+ * @author: jeecg-boot
+ */
+@Aspect
+@Component
+@Slf4j
+public class PermissionDataAspect {
+    @Lazy
+    @Autowired
+    private CommonAPI commonApi;
+
+    private static final String SPOT_DO = ".do";
+
+    @Pointcut("@annotation(org.jeecg.common.aspect.annotation.PermissionData)")
+    public void pointCut() {
+
+    }
+
+    @Around("pointCut()")
+    public Object arround(ProceedingJoinPoint point) throws  Throwable{
+        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Method method = signature.getMethod();
+        PermissionData pd = method.getAnnotation(PermissionData.class);
+        String component = pd.pageComponent();
+        String requestMethod = request.getMethod();
+        String requestPath = request.getRequestURI().substring(request.getContextPath().length());
+        requestPath = filterUrl(requestPath);
+        //update-begin-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效
+        //先判断是否online报表请求
+        if(requestPath.indexOf(UrlMatchEnum.CGREPORT_DATA.getMatchUrl())>=0 || requestPath.indexOf(UrlMatchEnum.CGREPORT_ONLY_DATA.getMatchUrl())>=0){
+            // 获取地址栏参数
+            String urlParamString = request.getParameter(CommonConstant.ONL_REP_URL_PARAM_STR);
+            if(oConvertUtils.isNotEmpty(urlParamString)){
+                requestPath+="?"+urlParamString;
+            }
+        }
+        //update-end-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效
+        log.debug("拦截请求 >> {} ; 请求类型 >> {} . ", requestPath, requestMethod);
+        String username = JwtUtil.getUserNameByToken(request);
+        //查询数据权限信息
+        //TODO 微服务情况下也得支持缓存机制
+        List<SysPermissionDataRuleModel> dataRules = commonApi.queryPermissionDataRule(component, requestPath, username);
+        if(dataRules!=null && dataRules.size()>0) {
+            //临时存储
+            JeecgDataAutorUtils.installDataSearchConditon(request, dataRules);
+            //TODO 微服务情况下也得支持缓存机制
+            SysUserCacheInfo userinfo = commonApi.getCacheUser(username);
+            JeecgDataAutorUtils.installUserInfo(request, userinfo);
+        }
+        return  point.proceed();
+    }
+
+    private String filterUrl(String requestPath){
+        String url = "";
+        if(oConvertUtils.isNotEmpty(requestPath)){
+            url = requestPath.replace("\\", "/");
+            url = url.replace("//", "/");
+            if(url.indexOf(SymbolConstant.DOUBLE_SLASH)>=0){
+                url = filterUrl(url);
+            }
+			/*if(url.startsWith("/")){
+				url=url.substring(1);
+			}*/
+        }
+        return url;
+    }
+
+    /**
+     * 获取请求地址
+     * @param request
+     * @return
+     */
+    @Deprecated
+    private String getJgAuthRequsetPath(HttpServletRequest request) {
+        String queryString = request.getQueryString();
+        String requestPath = request.getRequestURI();
+        if(oConvertUtils.isNotEmpty(queryString)){
+            requestPath += "?" + queryString;
+        }
+        // 去掉其他参数(保留一个参数) 例如:loginController.do?login
+        if (requestPath.indexOf(SymbolConstant.AND) > -1) {
+            requestPath = requestPath.substring(0, requestPath.indexOf("&"));
+        }
+        if(requestPath.indexOf(QueryRuleEnum.EQ.getValue())!=-1){
+            if(requestPath.indexOf(SPOT_DO)!=-1){
+                requestPath = requestPath.substring(0,requestPath.indexOf(".do")+3);
+            }else{
+                requestPath = requestPath.substring(0,requestPath.indexOf("?"));
+            }
+        }
+        // 去掉项目路径
+        requestPath = requestPath.substring(request.getContextPath().length() + 1);
+        return filterUrl(requestPath);
+    }
+
+    @Deprecated
+    private boolean moHuContain(List<String> list,String key){
+        for(String str : list){
+            if(key.contains(str)){
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+}

+ 68 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/UrlMatchEnum.java

@@ -0,0 +1,68 @@
+package org.jeecg.common.aspect;
+
+/**
+ * @Author scott
+ * @Date 2020/1/14 13:36
+ * @Description: 请求URL与菜单路由URL转换规则(方便于采用菜单路由URL来配置数据权限规则)
+ */
+public enum UrlMatchEnum {
+    /**求URL与菜单路由URL转换规则 /online/cgform/api/getData/ */
+    CGFORM_DATA("/online/cgform/api/getData/", "/online/cgformList/"),
+    /**求URL与菜单路由URL转换规则 /online/cgform/api/exportXls/ */
+    CGFORM_EXCEL_DATA("/online/cgform/api/exportXls/", "/online/cgformList/"),
+    /**求URL与菜单路由URL转换规则 /online/cgform/api/getTreeData/ */
+    CGFORM_TREE_DATA("/online/cgform/api/getTreeData/", "/online/cgformList/"),
+    /**求URL与菜单路由URL转换规则 /online/cgreport/api/getColumnsAndData/ */
+    CGREPORT_DATA("/online/cgreport/api/getColumnsAndData/", "/online/cgreport/"),
+    /** 求URL与菜单路由URL转换规则/online/cgreport/api/getData/ 【vue3报表数据请求地址】 */
+    CGREPORT_ONLY_DATA("/online/cgreport/api/getData/", "/online/cgreport/"),
+    /**求URL与菜单路由URL转换规则 /online/cgreport/api/exportXls/ */
+    CGREPORT_EXCEL_DATA("/online/cgreport/api/exportXls/", "/online/cgreport/"),
+    /**求URL与菜单路由URL转换规则 /online/cgreport/api/exportManySheetXls/ */
+    CGREPORT_EXCEL_DATA2("/online/cgreport/api/exportManySheetXls/", "/online/cgreport/");
+
+    UrlMatchEnum(String url, String matchUrl) {
+        this.url = url;
+        this.matchUrl = matchUrl;
+    }
+
+    /**
+     * Request 请求 URL前缀
+     */
+    private String url;
+    /**
+     * 菜单路由 URL前缀 (对应菜单路径)
+     */
+    private String matchUrl;
+
+    /**
+     * 根据req url 获取到菜单配置路径(前端页面路由URL)
+     *
+     * @param url
+     * @return
+     */
+    public static String getMatchResultByUrl(String url) {
+        //获取到枚举
+        UrlMatchEnum[] values = UrlMatchEnum.values();
+        //加强for循环进行遍历操作
+        for (UrlMatchEnum lr : values) {
+            //如果遍历获取的type和参数type一致
+            if (url.indexOf(lr.url) != -1) {
+                //返回type对象的desc
+                return url.replace(lr.url, lr.matchUrl);
+            }
+        }
+        return null;
+    }
+
+    public String getMatchUrl() {
+        return matchUrl;
+    }
+    //    public static void main(String[] args) {
+//        /**
+//         * 比如request真实请求URL: /online/cgform/api/getData/81fcf7d8922d45069b0d5ba983612d3a
+//         * 转换匹配路由URL后(对应配置的菜单路径):/online/cgformList/81fcf7d8922d45069b0d5ba983612d3a
+//         */
+//        System.out.println(UrlMatchEnum.getMatchResultByUrl("/online/cgform/api/getData/81fcf7d8922d45069b0d5ba983612d3a"));
+//    }
+}

+ 23 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoDict.java

@@ -0,0 +1,23 @@
+package org.jeecg.common.aspect.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 通过此注解声明的接口,自动实现字典翻译
+ * 
+ * @Author scott
+ * @email jeecgos@163.com
+ * @Date 2022年01月05日
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface AutoDict {
+
+	/**
+	 * 暂时无用
+	 * @return
+	 */
+	String value() default "";
+
+}

+ 47 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoLog.java

@@ -0,0 +1,47 @@
+package org.jeecg.common.aspect.annotation;
+
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.enums.ModuleType;
+
+import java.lang.annotation.*;
+
+/**
+ * 系统日志注解
+ *
+ * @Author scott
+ * @email jeecgos@163.com
+ * @Date 2019年1月14日
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface AutoLog {
+
+    /**
+     * 日志内容
+     *
+     * @return
+     */
+    String value() default "";
+
+    /**
+     * 日志类型
+     *
+     * @return 1:登录;2:操作;
+     */
+    int logType() default CommonConstant.LOG_TYPE_2;
+
+    /**
+     * 操作日志类型
+     *
+     * @return (1查询,2添加,3修改,4删除,5导入,6导出)
+     */
+    int operateType() default 0;
+
+    /**
+     * 模块类型 默认为common
+     *
+     * @return EMAIL:邮件
+     */
+    ModuleType module() default ModuleType.COMMON;
+}

+ 54 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/Dict.java

@@ -0,0 +1,54 @@
+package org.jeecg.common.aspect.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 字典注解
+ * @author: dangzhenghui
+ * @date: 2019年03月17日-下午9:37:16
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Dict {
+    /**
+     * 方法描述:  数据code
+     * 作    者: dangzhenghui
+     * 日    期: 2019年03月17日-下午9:37:16
+     *
+     * @return 返回类型: String
+     */
+    String dicCode();
+
+    /**
+     * 方法描述:  数据Text
+     * 作    者: dangzhenghui
+     * 日    期: 2019年03月17日-下午9:37:16
+     *
+     * @return 返回类型: String
+     */
+    String dicText() default "";
+
+    /**
+     * 方法描述: 数据字典表
+     * 作    者: dangzhenghui
+     * 日    期: 2019年03月17日-下午9:37:16
+     *
+     * @return 返回类型: String
+     */
+    String dictTable() default "";
+
+
+    //update-begin---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+    /**
+     * 方法描述: 数据字典表所在数据源名称
+     * 作    者: chenrui
+     * 日    期: 2023年12月20日-下午4:58
+     *
+     * @return 返回类型: String
+     */
+    String ds() default "";
+    //update-end---author:chenrui ---date:20231221  for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
+}

+ 32 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/DictFillIn.java

@@ -0,0 +1,32 @@
+package org.jeecg.common.aspect.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 字典值默认填充
+ * <ul>
+ *     <li>只填充数据字典的值</li>
+ *     <li>不填充数据库表的值</li>
+ * </ul>
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DictFillIn {
+
+    /**
+     * 字典编码
+     *
+     * @return 返回类型: String
+     */
+    String dicCode();
+
+    /**
+     * 取值字段
+     *
+     * @return 返回类型: String
+     */
+    String refField();
+}

+ 20 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/DynamicTable.java

@@ -0,0 +1,20 @@
+package org.jeecg.common.aspect.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 动态table切换
+ *
+ * @author :zyf
+ * @date:2020-04-25
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DynamicTable {
+    /**
+     * 需要动态解析的表名
+     * @return
+     */
+    String value();
+}

+ 19 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/OnlineAuth.java

@@ -0,0 +1,19 @@
+package org.jeecg.common.aspect.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * online请求拦截专用注解
+ * @author: jeecg-boot
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE,ElementType.METHOD})
+@Documented
+public @interface OnlineAuth {
+
+    /**
+     * 请求关键字,在xxx/code之前的字符串
+     * @return
+     */
+    String value();
+}

+ 25 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/PermissionData.java

@@ -0,0 +1,25 @@
+package org.jeecg.common.aspect.annotation;
+
+import java.lang.annotation.*;
+
+/**
+  *  数据权限注解
+ * @Author taoyan
+ * @Date 2019年4月11日
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE,ElementType.METHOD})
+@Documented
+public @interface PermissionData {
+	/**
+	 * 暂时没用
+	 * @return
+	 */
+	String value() default "";
+	
+	
+	/**
+	 * 配置菜单的组件路径,用于数据权限
+	 */
+	String pageComponent() default "";
+}

+ 327 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java

@@ -0,0 +1,327 @@
+package org.jeecg.common.constant;
+
+/**
+ * @Description: 通用常量
+ * @author: jeecg-boot
+ */
+public interface CommonConstant {
+
+    /** 删除标志 */
+    Integer DEL_FLAG_1 = 1;
+    /** 未删除 */
+    Integer DEL_FLAG_0 = 0;
+
+    /** 系统日志类型: 登录 */
+    int LOG_TYPE_1 = 1;
+    /** 系统日志类型: 操作 */
+    int LOG_TYPE_2 = 2;
+
+    /** 操作日志类型: 查询 */
+    int OPERATE_TYPE_1 = 1;
+    /** 操作日志类型: 添加 */
+    int OPERATE_TYPE_2 = 2;
+    /** 操作日志类型: 更新 */
+    int OPERATE_TYPE_3 = 3;
+    /** 操作日志类型: 删除 */
+    int OPERATE_TYPE_4 = 4;
+    /** 操作日志类型: 导入 */
+    int OPERATE_TYPE_5 = 5;
+    /** 操作日志类型: 导出 */
+    int OPERATE_TYPE_6 = 6;
+
+    /** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */
+    Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
+    /** {@code 404 Not Found} (HTTP/1.0 - RFC 1945) */
+    Integer SC_INTERNAL_NOT_FOUND_404 = 404;
+    /** {@code 200 OK} (HTTP/1.0 - RFC 1945) */
+    Integer SC_OK_200 = 200;
+
+    /** 访问权限认证未通过 510 */
+    Integer SC_JEECG_NO_AUTHZ = 510;
+
+    /** 登录用户Shiro权限缓存KEY前缀 */
+    String PREFIX_USER_SHIRO_CACHE = "shiro:cache:org.jeecg.config.shiro.ShiroRealm.authorizationCache:";
+    /** 登录用户Token令牌缓存KEY前缀 */
+    String PREFIX_USER_TOKEN = "prefix_user_token:";
+
+    /** 登录二维码 */
+    String LOGIN_QRCODE_PRE = "QRCODELOGIN:";
+    String LOGIN_QRCODE = "LQ:";
+    /** 登录二维码token */
+    String LOGIN_QRCODE_TOKEN = "LQT:";
+
+    /** 0:一级菜单 */
+    Integer MENU_TYPE_0 = 0;
+    /** 1:子菜单 */
+    Integer MENU_TYPE_1 = 1;
+    /** 2:按钮权限 */
+    Integer MENU_TYPE_2 = 2;
+
+    /** 通告对象类型(USER:指定用户,ALL:全体用户) */
+    String MSG_TYPE_UESR = "USER";
+    String MSG_TYPE_ALL = "ALL";
+
+    /** 发布状态(0未发布,1已发布,2已撤销) */
+    String NO_SEND = "0";
+    String HAS_SEND = "1";
+    String HAS_CANCLE = "2";
+
+    /** 阅读状态(0未读,1已读) */
+    Integer HAS_READ_FLAG = 1;
+    Integer NO_READ_FLAG = 0;
+
+    /** 优先级(L低,M中,H高) */
+    String PRIORITY_L = "L";
+    String PRIORITY_M = "M";
+    String PRIORITY_H = "H";
+
+    /** 短信模板方式  0 .登录模板、1.注册模板、2.忘记密码模板 */
+    String SMS_TPL_TYPE_0 = "0";
+    String SMS_TPL_TYPE_1 = "1";
+    String SMS_TPL_TYPE_2 = "2";
+
+    /** 状态(0无效1有效) */
+    String STATUS_0 = "0";
+    String STATUS_1 = "1";
+
+    /** 同步工作流引擎1同步0不同步 */
+    Integer ACT_SYNC_1 = 1;
+    Integer ACT_SYNC_0 = 0;
+
+    /** 消息类型1:通知公告2:系统消息 */
+    String MSG_CATEGORY_1 = "1";
+    String MSG_CATEGORY_2 = "2";
+
+    /** 是否配置菜单的数据权限 1是0否 */
+    Integer RULE_FLAG_0 = 0;
+    Integer RULE_FLAG_1 = 1;
+
+    /** 是否用户已被冻结 1正常(解冻) 2冻结 3离职 */
+    Integer USER_UNFREEZE = 1;
+    Integer USER_FREEZE = 2;
+    Integer USER_QUIT = 3;
+
+    /** 字典翻译文本后缀 */
+    String DICT_TEXT_SUFFIX = "_dictText";
+    /** 字典翻译颜色后缀 */
+    String DICT_COLOR_SUFFIX = "_dictColor";
+
+    /** 表单设计器主表类型 */
+    Integer DESIGN_FORM_TYPE_MAIN = 1;
+    /** 表单设计器子表表类型 */
+    Integer DESIGN_FORM_TYPE_SUB = 2;
+    /** 表单设计器URL授权通过 */
+    Integer DESIGN_FORM_URL_STATUS_PASSED = 1;
+    /** 表单设计器URL授权未通过 */
+    Integer DESIGN_FORM_URL_STATUS_NOT_PASSED = 2;
+    /** 表单设计器新增 Flag */
+    String DESIGN_FORM_URL_TYPE_ADD = "add";
+    /** 表单设计器修改 Flag */
+    String DESIGN_FORM_URL_TYPE_EDIT = "edit";
+    /** 表单设计器详情 Flag */
+    String DESIGN_FORM_URL_TYPE_DETAIL = "detail";
+    /** 表单设计器复用数据 Flag */
+    String DESIGN_FORM_URL_TYPE_REUSE = "reuse";
+    /** 表单设计器编辑 Flag (已弃用) */
+    String DESIGN_FORM_URL_TYPE_VIEW = "view";
+
+    /** online参数值设置(是:Y, 否:N) */
+    String ONLINE_PARAM_VAL_IS_TURE = "Y";
+    String ONLINE_PARAM_VAL_IS_FALSE = "N";
+
+    /** 文件上传类型(本地:local,Minio:minio,阿里云:alioss) */
+    String UPLOAD_TYPE_LOCAL = "local";
+    String UPLOAD_TYPE_MINIO = "minio";
+    String UPLOAD_TYPE_OSS = "alioss";
+
+    /** 文档上传自定义桶名称 */
+    String UPLOAD_CUSTOM_BUCKET = "eoafile";
+    /** 文档上传自定义路径 */
+    String UPLOAD_CUSTOM_PATH = "eoafile";
+    /** 文件外链接有效天数 */
+    Integer UPLOAD_EFFECTIVE_DAYS = 1;
+
+    /** 员工身份 (1:普通员工  2:上级) */
+    Integer USER_IDENTITY_1 = 1;
+    Integer USER_IDENTITY_2 = 2;
+
+    /** sys_user 表 username 唯一键索引 */
+    String SQL_INDEX_UNIQ_SYS_USER_USERNAME = "uniq_sys_user_username";
+    /** sys_user 表 work_no 唯一键索引 */
+    String SQL_INDEX_UNIQ_SYS_USER_WORK_NO = "uniq_sys_user_work_no";
+    /** sys_user 表 phone 唯一键索引 */
+    String SQL_INDEX_UNIQ_SYS_USER_PHONE = "uniq_sys_user_phone";
+    /** 达梦数据库升提示。违反表[SYS_USER]唯一性约束 */
+    String SQL_INDEX_UNIQ_SYS_USER = "唯一性约束";
+
+    /** sys_user 表 email 唯一键索引 */
+    String SQL_INDEX_UNIQ_SYS_USER_EMAIL = "uniq_sys_user_email";
+    /** sys_quartz_job 表 job_class_name 唯一键索引 */
+    String SQL_INDEX_UNIQ_JOB_CLASS_NAME = "uniq_job_class_name";
+    /** sys_position 表 code 唯一键索引 */
+    String SQL_INDEX_UNIQ_CODE = "uniq_code";
+    /** sys_role 表 code 唯一键索引 */
+    String SQL_INDEX_UNIQ_SYS_ROLE_CODE = "uniq_sys_role_role_code";
+    /** sys_depart 表 code 唯一键索引 */
+    String SQL_INDEX_UNIQ_DEPART_ORG_CODE = "uniq_depart_org_code";
+    /** sys_category 表 code 唯一键索引 */
+    String SQL_INDEX_UNIQ_CATEGORY_CODE = "idx_sc_code";
+
+    /** 在线聊天 是否为默认分组 */
+    String IM_DEFAULT_GROUP = "1";
+    /** 在线聊天 图片文件保存路径 */
+    String IM_UPLOAD_CUSTOM_PATH = "biz/user_imgs";
+    /** 在线聊天 用户状态 */
+    String IM_STATUS_ONLINE = "online";
+    /** 在线聊天 SOCKET消息类型 */
+    String IM_SOCKET_TYPE = "chatMessage";
+    /** 在线聊天 是否开启默认添加好友 1是 0否 */
+    String IM_DEFAULT_ADD_FRIEND = "1";
+    /** 在线聊天 用户好友缓存前缀 */
+    String IM_PREFIX_USER_FRIEND_CACHE = "sys:cache:im:im_prefix_user_friend_";
+
+    /** 考勤补卡业务状态 (1:同意  2:不同意) */
+    String SIGN_PATCH_BIZ_STATUS_1 = "1";
+    String SIGN_PATCH_BIZ_STATUS_2 = "2";
+
+    /** 公文文档上传自定义路径 */
+    String UPLOAD_CUSTOM_PATH_OFFICIAL = "officialdoc";
+    /** 公文文档下载自定义路径 */
+    String DOWNLOAD_CUSTOM_PATH_OFFICIAL = "officaldown";
+
+    /** WPS存储值类别(1 code文号 2 text(WPS模板还是公文发文模板)) */
+    String WPS_TYPE_1 = "1";
+    String WPS_TYPE_2 = "2";
+
+    /**===============================================================================================*/
+    /**
+     * ::非常重要::
+     * 注意:这四个常量值如果修改,需要与 jeecg-boot-starter/jeecg-boot-common/org.jeecg.config.FeignConfig 类中的值保持一致。
+     */
+    String X_ACCESS_TOKEN = "X-Access-Token";
+    String X_SIGN = "X-Sign";
+    String X_TIMESTAMP = "X-TIMESTAMP";
+    /** 简流接口请求头,用于排除不支持的控件字段 */
+    String X_MiniFlowExclusionFieldMode = "X-Miniflowexclusionfieldmode";
+    /** =============================================================================================== */
+
+    String TOKEN_IS_INVALID_MSG = "Token失效,请重新登录!";
+    String X_FORWARDED_SCHEME = "X-Forwarded-Scheme";
+
+    /** 微服务读取配置文件属性 服务地址 */
+    String CLOUD_SERVER_KEY = "spring.cloud.nacos.discovery.server-addr";
+
+    /** 第三方登录 验证密码/创建用户 都需要设置一个操作码 防止被恶意调用 */
+    String THIRD_LOGIN_CODE = "third_login_code";
+
+    /** 第三方APP同步方向:本地 --> 第三方APP */
+    String THIRD_SYNC_TO_APP = "SYNC_TO_APP";
+    /** 第三方APP同步方向:第三方APP --> 本地 */
+    String THIRD_SYNC_TO_LOCAL = "SYNC_TO_LOCAL";
+
+    /** 系统通告消息状态:0=未发布 */
+    String ANNOUNCEMENT_SEND_STATUS_0 = "0";
+    /** 系统通告消息状态:1=已发布 */
+    String ANNOUNCEMENT_SEND_STATUS_1 = "1";
+    /** 系统通告消息状态:2=已撤销 */
+    String ANNOUNCEMENT_SEND_STATUS_2 = "2";
+
+    /** ONLINE 报表权限用 从request中获取地址栏后的参数 */
+    String ONL_REP_URL_PARAM_STR = "onlRepUrlParamStr";
+
+    /** POST请求 */
+    String HTTP_POST = "POST";
+    /** PUT请求 */
+    String HTTP_PUT = "PUT";
+    /** PATCH请求 */
+    String HTTP_PATCH = "PATCH";
+    /** 未知的 */
+    String UNKNOWN = "unknown";
+    /** 字符串http */
+    String STR_HTTP = "http";
+    /** String 类型的空值 */
+    String STRING_NULL = "null";
+    /** 前端vue3版本Header参数名 */
+    String VERSION = "X-Version";
+    String VERSION_V3 = "v3";
+    /** 存储在线程变量里的动态表名 */
+    String DYNAMIC_TABLE_NAME = "DYNAMIC_TABLE_NAME";
+    /** http:// http协议 */
+    String HTTP_PROTOCOL = "http://";
+    /** https:// https协议 */
+    String HTTPS_PROTOCOL = "https://";
+
+    /** 部门表唯一key,id */
+    String DEPART_KEY_ID = "id";
+    /** 部门表唯一key,orgCode */
+    String DEPART_KEY_ORG_CODE = "orgCode";
+
+    /**======【消息推送相关】==============================================================================*/
+    /** 发消息 会传递一些信息到map */
+    String NOTICE_MSG_SUMMARY = "NOTICE_MSG_SUMMARY";
+    /** 发消息 会传递一个业务ID到map */
+    String NOTICE_MSG_BUS_ID = "NOTICE_MSG_BUS_ID";
+    /** 发消息 消息业务类型 */
+    String NOTICE_MSG_BUS_TYPE = "NOTICE_MSG_BUS_TYPE";
+    /** 邮箱消息中地址登录时地址后携带的token,需要替换成真实的token值 */
+    String LOGIN_TOKEN = "{LOGIN_TOKEN}";
+    /** 模板消息中 跳转地址的对应的key */
+    String MSG_HREF_URL = "url";
+    /** sys_data_log表的类型 用于区别评论区域的日志数据 */
+    String DATA_LOG_TYPE_COMMENT = "comment";
+    /** sys_data_log表的类型 老的数据比较 类型都设置为json */
+    String DATA_LOG_TYPE_JSON = "json";
+    /** 消息模板:markdown */
+    String MSG_TEMPLATE_TYPE_MD = "5";
+    /**========【消息推送相关】==========================================================================*/
+
+    /** 短信验证码redis-key的前缀 */
+    String PHONE_REDIS_KEY_PRE = "phone_msg";
+    /** 是文件夹 */
+    String IT_IS_FOLDER = "1";
+    /** 文件拥有者 */
+    String FILE_OWNER = "owner";
+    /** 文件管理员 */
+    String FILE_ADMIN = "admin";
+    /** 只允许编辑 */
+    String FILE_EDITABLE = "editable";
+    /** 登录失败,用于记录失败次数的key */
+    String LOGIN_FAIL = "LOGIN_FAIL_";
+    /** 入职事件 */
+    Integer BPM_USER_EVENT_ADD = 1;
+    /** 离职事件 */
+    Integer BPM_USER_EVENT_LEVEL = 2;
+    /** 不是叶子节点 */
+    Integer NOT_LEAF = 0;
+    /** 是叶子节点 */
+    Integer IS_LEAF = 1;
+
+    /** 钉钉 */
+    String DINGTALK = "DINGTALK";
+
+    /** 【low-app用】 应用级别的复制 */
+    String COPY_LEVEL_APP = "app";
+    /** 【low-app用】 菜单级别的复制 */
+    String COPY_LEVEL_MENU = "menu";
+    /** 【low-app用】 应用备份 */
+    String COPY_LEVEL_BAK = "backup";
+    /** 【low-app用】 从备份还原 */
+    String COPY_LEVEL_COVER = "cover";
+
+    /** 【QQYUN-6034】关联字段变更历史值,缓存半个小时 */
+    String CACHE_REL_FIELD_OLD_VAL = "sys:cache:desform:relFieldOldVal:";
+
+    /** 排序类型:升序 */
+    String ORDER_TYPE_ASC = "ASC";
+    /** 排序类型:降序 */
+    String ORDER_TYPE_DESC = "DESC";
+
+    // update-begin---author:scott ---date:2023-09-10  for:积木报表常量----
+    /** 报表允许设计开发的角色 */
+    String[] allowDevRoles = new String[]{"lowdeveloper", "admin"};
+
+    /** 【对应积木报表的常量】 数据隔离模式: 按照创建人隔离 */
+    final String SAAS_MODE_CREATED = "created";
+    // update-end---author:scott ---date::2023-09-10  for:积木报表常量----
+
+}

+ 48 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonSendStatus.java

@@ -0,0 +1,48 @@
+package org.jeecg.common.constant;
+
+/**
+ * 	系统通告 - 发布状态
+ * @Author LeeShaoQing
+ *
+ */
+public interface CommonSendStatus {
+
+    /**
+     * 未发布
+     */
+    public static final String UNPUBLISHED_STATUS_0 = "0";
+
+    /**
+     * 已发布
+     */
+	public static final String PUBLISHED_STATUS_1 = "1";
+
+    /**
+     * 撤销
+     */
+	public static final String REVOKE_STATUS_2 = "2";
+
+    /**
+     * app端推送会话标识后缀
+     */
+	public static final String  APP_SESSION_SUFFIX = "_app";
+
+
+	/**-----【流程相关通知模板code】------------------------------------------------------------*/
+	/**流程催办——系统通知消息模板*/
+	public static final String TZMB_BPM_CUIBAN = "bpm_cuiban";
+	/**流程抄送——系统通知消息模板*/
+	public static final String TZMB_BPM_CC = "bpm_cc";
+	/**流程催办——邮件通知消息模板*/
+	public static final String TZMB_BPM_CUIBAN_EMAIL = "bpm_cuiban_email";
+	/**标准模板—系统消息通知*/
+	public static final String TZMB_SYS_TS_NOTE = "sys_ts_note";
+	/**流程超时提醒——系统通知消息模板*/
+	public static final String TZMB_BPM_CHAOSHI_TIP = "bpm_chaoshi_tip";
+	/**-----【流程相关通知模板code】-----------------------------------------------------------*/
+
+	/**
+	 * 系统通知拓展参数(比如:用于流程抄送和催办通知,这里额外传递流程跳转页面所需要的路由参数)
+	 */
+	public static final String MSG_ABSTRACT_JSON = "msg_abstract";
+}

+ 159 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DataBaseConstant.java

@@ -0,0 +1,159 @@
+package org.jeecg.common.constant;
+/**
+ * 数据库上下文常量
+ * @author: jeecg-boot
+ */
+public interface DataBaseConstant {
+	//*********数据库类型****************************************
+
+    /**MYSQL数据库*/
+	public static final String DB_TYPE_MYSQL = "MYSQL";
+
+    /** ORACLE*/
+	public static final String DB_TYPE_ORACLE = "ORACLE";
+
+    /**达梦数据库*/
+	public static final String DB_TYPE_DM = "DM";
+
+    /**postgreSQL达梦数据库*/
+	public static final String DB_TYPE_POSTGRESQL = "POSTGRESQL";
+	
+    /**人大金仓数据库*/
+	public static final String DB_TYPE_KINGBASEES = "KINGBASEES";
+
+    /**sqlserver数据库*/
+	public static final String DB_TYPE_SQLSERVER = "SQLSERVER";
+
+    /**mariadb 数据库*/
+	public static final String DB_TYPE_MARIADB = "MARIADB";
+
+    /**DB2 数据库*/
+	public static final String DB_TYPE_DB2 = "DB2";
+
+    /**HSQL 数据库*/
+	public static final String DB_TYPE_HSQL = "HSQL";
+
+//	// 数据库类型,对应 database_type 字典
+//	public static final String DB_TYPE_MYSQL_NUM = "1";
+//	public static final String DB_TYPE_MYSQL7_NUM = "6";
+//	public static final String DB_TYPE_ORACLE_NUM = "2";
+//	public static final String DB_TYPE_SQLSERVER_NUM = "3";
+//	public static final String DB_TYPE_POSTGRESQL_NUM = "4";
+//	public static final String DB_TYPE_MARIADB_NUM = "5";
+
+	//*********系统上下文变量****************************************
+	/**
+	 * 数据-所属机构编码
+	 */
+	public static final String SYS_ORG_CODE = "sysOrgCode";
+	/**
+	 * 数据-所属机构编码
+	 */
+	public static final String SYS_ORG_CODE_TABLE = "sys_org_code";
+	/**
+	 * 数据-所属机构编码
+	 */
+	public static final String SYS_MULTI_ORG_CODE = "sysMultiOrgCode";
+	/**
+	 * 数据-所属机构编码
+	 */
+	public static final String SYS_MULTI_ORG_CODE_TABLE = "sys_multi_org_code";
+	/**
+	 * 数据-系统用户编码(对应登录用户账号)
+	 */
+	public static final String SYS_USER_CODE = "sysUserCode";
+	/**
+	 * 数据-系统用户编码(对应登录用户账号)
+	 */
+	public static final String SYS_USER_CODE_TABLE = "sys_user_code";
+	
+	/**
+	 * 登录用户真实姓名
+	 */
+	public static final String SYS_USER_NAME = "sysUserName";
+	/**
+	 * 登录用户真实姓名
+	 */
+	public static final String SYS_USER_NAME_TABLE = "sys_user_name";
+	/**
+	 * 系统日期"yyyy-MM-dd"
+	 */
+	public static final String SYS_DATE = "sysDate";
+	/**
+	 * 系统日期"yyyy-MM-dd"
+	 */
+	public static final String SYS_DATE_TABLE = "sys_date";
+	/**
+	 * 系统时间"yyyy-MM-dd HH:mm"
+	 */
+	public static final String SYS_TIME = "sysTime";
+	/**
+	 * 系统时间"yyyy-MM-dd HH:mm"
+	 */
+	public static final String SYS_TIME_TABLE = "sys_time";
+	/**
+	 * 数据-所属机构编码
+	 */
+	public static final String SYS_BASE_PATH = "sys_base_path";
+	//*********系统上下文变量****************************************
+	
+	
+	//*********系统建表标准字段****************************************
+	/**
+	 * 创建者登录名称
+	 */
+	public static final String CREATE_BY_TABLE = "create_by";
+	/**
+	 * 创建者登录名称
+	 */
+	public static final String CREATE_BY = "createBy";
+	/**
+	 * 创建日期时间
+	 */
+	public static final String CREATE_TIME_TABLE = "create_time";
+	/**
+	 * 创建日期时间
+	 */
+	public static final String CREATE_TIME = "createTime";
+	/**
+	 * 更新用户登录名称
+	 */
+	public static final String UPDATE_BY_TABLE = "update_by";
+	/**
+	 * 更新用户登录名称
+	 */
+	public static final String UPDATE_BY = "updateBy";
+	/**
+	 * 更新日期时间
+	 */
+	public static final String UPDATE_TIME = "updateTime";
+	/**
+	 * 更新日期时间
+	 */
+	public static final String UPDATE_TIME_TABLE = "update_time";
+	
+	/**
+	 * 业务流程状态
+	 */
+	public static final String BPM_STATUS = "bpmStatus";
+	/**
+	 * 业务流程状态
+	 */
+	public static final String BPM_STATUS_TABLE = "bpm_status";
+	//*********系统建表标准字段****************************************
+
+    /**
+     * sql语句 where
+     */
+    String SQL_WHERE = "where";
+
+    /**
+     * sql语句 asc
+     */
+    String SQL_ASC = "asc";
+
+    /**
+     * sqlserver数据库,中间有空格
+     */
+    String DB_TYPE_SQL_SERVER_BLANK = "sql server";
+}

+ 25 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/FillRuleConstant.java

@@ -0,0 +1,25 @@
+package org.jeecg.common.constant;
+
+/**
+ * 规则值生成 编码常量类
+ * @author: taoyan
+ * @date: 2020年04月02日
+ */
+public class FillRuleConstant {
+
+    /**
+     * 公文发文编码
+     */
+    public static final String DOC_SEND = "doc_send_code";
+
+    /**
+     * 部门编码
+     */
+    public static final String DEPART = "org_num_role";
+
+    /**
+     * 分类字典编码
+     */
+    public static final String CATEGORY = "category_code_rule";
+
+}

+ 54 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ServiceNameConstants.java

@@ -0,0 +1,54 @@
+/*
+ *
+ *  *  Copyright (c) 2019-2020, 冷冷 (wangiegie@gmail.com).
+ *  *  <p>
+ *  *  Licensed under the GNU Lesser General Public License 3.0 (the "License");
+ *  *  you may not use this file except in compliance with the License.
+ *  *  You may obtain a copy of the License at
+ *  *  <p>
+ *  * https://www.gnu.org/licenses/lgpl.html
+ *  *  <p>
+ *  * 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 org.jeecg.common.constant;
+
+/**
+ * @author scott
+ * @date 2019年05月18日
+ * 服务名称
+ */
+public interface ServiceNameConstants {
+
+	/**
+	 * 微服务名:系统管理模块
+	 */
+	String SERVICE_SYSTEM = "jeecg-system";
+	/**
+	 * 微服务名: demo模块
+	 */
+	String SERVICE_DEMO = "jeecg-demo";
+	/**
+	 * 微服务名:online在线模块
+	 */
+	String SERVICE_ONLINE = "jeecg-online";
+	/**
+	 * 微服务名:OA模块
+	 */
+	String SERVICE_EOA = "jeecg-eoa";
+	/**
+	 * 微服务名:表单设计模块
+	 */
+	String SERVICE_FORM = "jeecg-desform";
+
+	/**
+	 * gateway通过header传递根路径 basePath
+	 */
+	String X_GATEWAY_BASE_PATH = "X_GATEWAY_BASE_PATH";
+
+}

+ 123 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/SymbolConstant.java

@@ -0,0 +1,123 @@
+package org.jeecg.common.constant;
+
+/**
+ * @Description: 符号和特殊符号常用类
+ * @author: wangshuai
+ * @date: 2022年03月30日 17:44
+ */
+public class SymbolConstant {
+
+    /**
+     * 符号:点
+     */
+    public static final String SPOT = ".";
+
+    /**
+     * 符号:双斜杠
+     */
+    public static final String DOUBLE_BACKSLASH = "\\";
+
+    /**
+     * 符号:冒号
+     */
+    public static final String COLON = ":";
+
+    /**
+     * 符号:逗号
+     */
+    public static final String COMMA = ",";
+
+    /**
+     * 符号:左花括号 }
+     */
+    public static final String LEFT_CURLY_BRACKET = "{";
+
+    /**
+     * 符号:右花括号 }
+     */
+    public static final String RIGHT_CURLY_BRACKET = "}";
+
+    /**
+     * 符号:井号 #
+     */
+    public static final String WELL_NUMBER = "#";
+
+    /**
+     * 符号:单斜杠
+     */
+    public static final String SINGLE_SLASH = "/";
+
+    /**
+     * 符号:双斜杠
+     */
+    public static final String DOUBLE_SLASH = "//";
+
+    /**
+     * 符号:感叹号
+     */
+    public static final String EXCLAMATORY_MARK = "!";
+
+    /**
+     * 符号:下划线
+     */
+    public static final String UNDERLINE = "_";
+
+    /**
+     * 符号:单引号
+     */
+    public static final String SINGLE_QUOTATION_MARK = "'";
+
+    /**
+     * 符号:星号
+     */
+    public static final String ASTERISK = "*";
+
+    /**
+     * 符号:百分号
+     */
+    public static final String PERCENT_SIGN = "%";
+
+    /**
+     * 符号:美元 $
+     */
+    public static final String DOLLAR = "$";
+
+    /**
+     * 符号:和 &
+     */
+    public static final String AND = "&";
+
+    /**
+     * 符号:../
+     */
+    public static final String SPOT_SINGLE_SLASH = "../";
+
+    /**
+     * 符号:..\\
+     */
+    public static final String SPOT_DOUBLE_BACKSLASH = "..\\";
+
+    /**
+     * 系统变量前缀 #{
+     */
+    public static final String SYS_VAR_PREFIX = "#{";
+
+    /**
+     * 符号 {{
+     */
+    public static final String DOUBLE_LEFT_CURLY_BRACKET = "{{";
+
+    /**
+     * 符号:[
+     */
+    public static final String SQUARE_BRACKETS_LEFT = "[";
+    /**
+     * 符号:]
+     */
+    public static final String SQUARE_BRACKETS_RIGHT = "]";
+
+    /**
+     * 拼接字符串符号 分号 ;
+     */
+    public static final String SEMICOLON = ";";
+}

+ 71 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WebsocketConst.java

@@ -0,0 +1,71 @@
+package org.jeecg.common.constant;
+
+/**
+ * @Description: Websocket常量类
+ * @author: taoyan
+ * @date: 2020年03月23日
+ */
+public class WebsocketConst {
+
+
+    /**
+     * 消息json key:cmd
+     */
+    public static final String MSG_CMD = "cmd";
+
+    /**
+     * 消息json key:msgId
+     */
+    public static final String MSG_ID = "msgId";
+
+    /**
+     * 消息json key:msgTxt
+     */
+    public static final String MSG_TXT = "msgTxt";
+
+    /**
+     * 消息json key:msgType
+     */
+    public static final String MSG_TYPE = "msgType";
+
+    /**
+     * 消息json key:userId
+     */
+    public static final String MSG_USER_ID = "userId";
+
+    /**
+     * 消息json key:chat
+     */
+    public static final String MSG_CHAT = "chat";
+
+    /**
+     * 消息类型 heartcheck
+     */
+    public static final String CMD_CHECK = "heartcheck";
+
+    /**
+     * 消息类型 user 用户消息
+     */
+    public static final String CMD_USER = "user";
+
+    /**
+     * 消息类型 topic 系统通知
+     */
+    public static final String CMD_TOPIC = "topic";
+
+    /**
+     * 消息类型 email
+     */
+    public static final String CMD_EMAIL = "email";
+
+    /**
+     * 消息类型 meetingsign 会议签到
+     */
+    public static final String CMD_SIGN = "sign";
+
+    /**
+     * 消息类型 新闻发布/取消
+     */
+    public static final String NEWS_PUBLISH = "publish";
+
+}

+ 167 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/CgformEnum.java

@@ -0,0 +1,167 @@
+package org.jeecg.common.constant.enums;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * online表单枚举 代码生成器用到
+ * @author: jeecg-boot
+ */
+public enum CgformEnum {
+
+    /**
+     * 单表
+     */
+    ONE(1, "one", "/jeecg/code-template-online", "default.one", "经典风格", new String[]{"vue3","vue","vue3Native"}),
+
+    /**
+     * 多表
+     */
+    MANY(2, "many", "/jeecg/code-template-online", "default.onetomany", "经典风格" ,new String[]{"vue"}),
+    /**
+     * 多表(jvxe风格)
+     *  */
+    JVXE_TABLE(2, "jvxe", "/jeecg/code-template-online", "jvxe.onetomany", "JVXE风格" ,new String[]{"vue3","vue","vue3Native"}),
+
+    /**
+     * 多表 (erp风格)
+     */
+    ERP(2, "erp", "/jeecg/code-template-online", "erp.onetomany", "ERP风格" ,new String[]{"vue3","vue","vue3Native"}),
+    /**
+     * 多表(内嵌子表风格)
+     */
+    INNER_TABLE(2, "innerTable", "/jeecg/code-template-online", "inner-table.onetomany", "内嵌子表风格" ,new String[]{"vue3","vue"}),
+    /**
+     * 多表(tab风格)
+     *  */
+    TAB(2, "tab", "/jeecg/code-template-online", "tab.onetomany", "Tab风格" ,new String[]{"vue3","vue"}),
+    /**
+     * 树形列表
+     */
+    TREE(3, "tree", "/jeecg/code-template-online", "default.tree", "树形列表" ,new String[]{"vue3","vue","vue3Native"});
+
+    /**
+     * 类型 1/单表 2/一对多 3/树
+     */
+    int type;
+    /**
+     * 编码标识
+     */
+    String code;
+    /**
+     * 代码生成器模板路径
+     */
+    String templatePath;
+    /**
+     * 代码生成器模板路径
+     */
+    String stylePath;
+    /**
+     * 模板风格名称
+     */
+    String note;
+    /**
+     * 支持代码风格 vue3:vue3包装代码 vue3Native:vue3原生代码 vue:vue2代码
+     */
+    String[] vueStyle;
+
+    /**
+     * 构造器
+     *
+     * @param type 类型 1/单表 2/一对多 3/树
+     * @param code 模板编码
+     * @param templatePath  模板路径
+     * @param stylePath  模板子路径
+     * @param note
+     * @param vueStyle 支持代码风格
+     */
+    CgformEnum(int type, String code, String templatePath, String stylePath, String note, String[] vueStyle) {
+        this.type = type;
+        this.code = code;
+        this.templatePath = templatePath;
+        this.stylePath = stylePath;
+        this.note = note;
+        this.vueStyle = vueStyle;
+    }
+
+    /**
+     * 根据code获取模板路径
+     *
+     * @param code
+     * @return
+     */
+    public static String getTemplatePathByConfig(String code) {
+        return getCgformEnumByConfig(code).templatePath;
+    }
+
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    public String getTemplatePath() {
+        return templatePath;
+    }
+
+    public void setTemplatePath(String templatePath) {
+        this.templatePath = templatePath;
+    }
+
+    public String getStylePath() {
+        return stylePath;
+    }
+
+    public void setStylePath(String stylePath) {
+        this.stylePath = stylePath;
+    }
+
+    public String[] getVueStyle() {
+        return vueStyle;
+    }
+
+    public void setVueStyle(String[] vueStyle) {
+        this.vueStyle = vueStyle;
+    }
+
+    /**
+     * 根据code找枚举
+     *
+     * @param code
+     * @return
+     */
+    public static CgformEnum getCgformEnumByConfig(String code) {
+        for (CgformEnum e : CgformEnum.values()) {
+            if (e.code.equals(code)) {
+                return e;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 根据类型找所有
+     *
+     * @param type
+     * @return
+     */
+    public static List<Map<String, Object>> getJspModelList(int type) {
+        List<Map<String, Object>> ls = new ArrayList<Map<String, Object>>();
+        for (CgformEnum e : CgformEnum.values()) {
+            if (e.type == type) {
+                Map<String, Object> map = new HashMap<String, Object>();
+                map.put("code", e.code);
+                map.put("note", e.note);
+                ls.add(map);
+            }
+        }
+        return ls;
+    }
+
+
+}

+ 81 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/DySmsEnum.java

@@ -0,0 +1,81 @@
+package org.jeecg.common.constant.enums;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * @Description: 短信枚举类
+ * @author: jeecg-boot
+ */
+public enum DySmsEnum {
+
+    /**登录短信模板编码*/
+	LOGIN_TEMPLATE_CODE("SMS_175435174","敲敲云","code"),
+    /**忘记密码短信模板编码*/
+	FORGET_PASSWORD_TEMPLATE_CODE("SMS_175435174","敲敲云","code"),
+	/**注册账号短信模板编码*/
+	REGISTER_TEMPLATE_CODE("SMS_175430166","敲敲云","code"),
+	/**会议通知*/
+	MEET_NOTICE_TEMPLATE_CODE("SMS_201480469","JEECG","username,title,minute,time"),
+	/**我的计划通知*/
+	PLAN_NOTICE_TEMPLATE_CODE("SMS_201470515","JEECG","username,title,time"),
+	/**支付成功短信通知*/
+	PAY_SUCCESS_NOTICE_CODE("SMS_461735163","敲敲云","realname,money,endTime"),
+	/**会员到期通知提醒*/
+	VIP_EXPIRE_NOTICE_CODE("SMS_461885023","敲敲云","realname,endTime");
+
+	/**
+	 * 短信模板编码
+	 */
+	private String templateCode;
+	/**
+	 * 签名
+	 */
+	private String signName;
+	/**
+	 * 短信模板必需的数据名称,多个key以逗号分隔,此处配置作为校验
+	 */
+	private String keys;
+	
+	private DySmsEnum(String templateCode,String signName,String keys) {
+		this.templateCode = templateCode;
+		this.signName = signName;
+		this.keys = keys;
+	}
+	
+	public String getTemplateCode() {
+		return templateCode;
+	}
+	
+	public void setTemplateCode(String templateCode) {
+		this.templateCode = templateCode;
+	}
+	
+	public String getSignName() {
+		return signName;
+	}
+	
+	public void setSignName(String signName) {
+		this.signName = signName;
+	}
+	
+	public String getKeys() {
+		return keys;
+	}
+
+	public void setKeys(String keys) {
+		this.keys = keys;
+	}
+
+	public static DySmsEnum toEnum(String templateCode) {
+		if(StringUtils.isEmpty(templateCode)){
+			return null;
+		}
+		for(DySmsEnum item : DySmsEnum.values()) {
+			if(item.getTemplateCode().equals(templateCode)) {
+				return item;
+			}
+		}
+		return null;
+	}
+}
+

+ 66 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/EmailTemplateEnum.java

@@ -0,0 +1,66 @@
+package org.jeecg.common.constant.enums;
+
+import org.jeecg.common.util.oConvertUtils;
+
+/**
+ * 邮件html模板配置地址美剧
+ *
+ * @author: liusq
+ * @Date: 2023-10-13
+ */
+public enum EmailTemplateEnum {
+    /**
+     * 流程催办
+     */
+    BPM_CUIBAN_EMAIL("bpm_cuiban_email", "/templates/email/bpm_cuiban_email.ftl"),
+    /**
+     * 流程新任务
+     */
+    BPM_NEW_TASK_EMAIL("bpm_new_task_email", "/templates/email/bpm_new_task_email.ftl"),
+    /**
+     * 表单新增记录
+     */
+    DESFORM_NEW_DATA_EMAIL("desform_new_data_email", "/templates/email/desform_new_data_email.ftl");
+
+    /**
+     * 模板名称
+     */
+    private String name;
+    /**
+     * 模板地址
+     */
+    private String url;
+
+    EmailTemplateEnum(String name, String url) {
+        this.name = name;
+        this.url = url;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public static EmailTemplateEnum getByName(String name) {
+        if (oConvertUtils.isEmpty(name)) {
+            return null;
+        }
+        for (EmailTemplateEnum val : values()) {
+            if (val.getName().equals(name)) {
+                return val;
+            }
+        }
+        return null;
+    }
+}

+ 76 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/FileTypeEnum.java

@@ -0,0 +1,76 @@
+package org.jeecg.common.constant.enums;
+
+import org.jeecg.common.util.oConvertUtils;
+
+/**
+ * 文件类型
+ */
+public enum FileTypeEnum {
+    //    文档类型(folder:文件夹 excel:excel doc:word pp:ppt image:图片  archive:其他文档 video:视频 voice:语音)
+//    FOLDER
+    xls(".xls","excel","excel"),
+    xlsx(".xlsx","excel","excel"),
+    doc(".doc","doc","word"),
+    docx(".docx","doc","word"),
+    ppt(".ppt","pp","ppt"),
+    pptx(".pptx","pp","ppt"),
+    gif(".gif","image","图片"),
+    jpg(".jpg","image","图片"),
+    jpeg(".jpeg","image","图片"),
+    png(".png","image","图片"),
+    txt(".txt","text","文本"),
+    avi(".avi","video","视频"),
+    mov(".mov","video","视频"),
+    rmvb(".rmvb","video","视频"),
+    rm(".rm","video","视频"),
+    flv(".flv","video","视频"),
+    mp4(".mp4","video","视频"),
+    zip(".zip","zip","压缩包"),
+    pdf(".pdf","pdf","pdf"),
+    mp3(".mp3","mp3","语音");
+
+    private String type;
+    private String value;
+    private String text;
+    private FileTypeEnum(String type,String value,String text){
+        this.type = type;
+        this.value = value;
+        this.text = text;
+    }
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public static FileTypeEnum getByType(String type){
+        if (oConvertUtils.isEmpty(type)) {
+            return null;
+        }
+        for (FileTypeEnum val : values()) {
+            if (val.getType().equals(type)) {
+                return val;
+            }
+        }
+        return null;
+    }
+
+}

+ 84 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/MessageTypeEnum.java

@@ -0,0 +1,84 @@
+package org.jeecg.common.constant.enums;
+
+import org.jeecg.common.system.annotation.EnumDict;
+import org.jeecg.common.system.vo.DictModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 消息类型
+ * @author: jeecg-boot
+ */
+@EnumDict("messageType")
+public enum MessageTypeEnum {
+
+    /** 系统消息 */
+    XT("system",  "系统消息"),
+    /** 钉钉消息 */
+    DD("dingtalk", "钉钉消息");
+
+    MessageTypeEnum(String type, String note){
+        this.type = type;
+        this.note = note;
+    }
+
+    /**
+     * 消息类型
+     */
+    String type;
+
+    /**
+     * 类型说明
+     */
+    String note;
+
+    public String getNote() {
+        return note;
+    }
+
+    public void setNote(String note) {
+        this.note = note;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    /**
+     * 获取字典数据
+     * @return
+     */
+    public static List<DictModel> getDictList(){
+        List<DictModel> list = new ArrayList<>();
+        DictModel dictModel = null;
+        for(MessageTypeEnum e: MessageTypeEnum.values()){
+            dictModel = new DictModel();
+            dictModel.setValue(e.getType());
+            dictModel.setText(e.getNote());
+            list.add(dictModel);
+        }
+        return list;
+    }
+
+    /**
+     * 根据type获取枚举
+     *
+     * @param type
+     * @return
+     */
+    public static MessageTypeEnum valueOfType(String type) {
+        for (MessageTypeEnum e : MessageTypeEnum.values()) {
+            if (e.getType().equals(type)) {
+                return e;
+            }
+        }
+        return null;
+    }
+
+}

+ 25 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/ModuleType.java

@@ -0,0 +1,25 @@
+package org.jeecg.common.constant.enums;
+
+/**
+ * 日志按模块分类
+ *
+ * @author: jeecg-boot
+ */
+public enum ModuleType {
+
+    /**
+     * 普通
+     */
+    COMMON,
+
+    /**
+     * online
+     */
+    ONLINE,
+
+    /**
+     * 邮件
+     * 使用这个标识的原因是日志不记录参数内容
+     */
+    EMAIL;
+}

+ 99 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/OperateTypeEnum.java

@@ -0,0 +1,99 @@
+package org.jeecg.common.constant.enums;
+
+import org.jeecg.common.constant.CommonConstant;
+
+/**
+ * @Description: 操作类型
+ * @author: jeecg-boot
+ * @date: 2022/3/31 10:05
+ */
+public enum OperateTypeEnum {
+
+    /**
+     * 列表/分页/查询
+     */
+    LIST(CommonConstant.OPERATE_TYPE_1, "list"),
+    PAGE(CommonConstant.OPERATE_TYPE_1, "page"),
+    QUERY(CommonConstant.OPERATE_TYPE_1, "query"),
+    GET(CommonConstant.OPERATE_TYPE_1, "get"),
+    FIND(CommonConstant.OPERATE_TYPE_1, "find"),
+
+    /**
+     * 新增
+     */
+    ADD(CommonConstant.OPERATE_TYPE_2, "add"),
+
+    /**
+     * 编辑
+     */
+    EDIT(CommonConstant.OPERATE_TYPE_3, "edit"),
+
+    /**
+     * 删除
+     */
+    DELETE(CommonConstant.OPERATE_TYPE_4, "delete"),
+
+    /**
+     * 导入
+     */
+    IMPORT(CommonConstant.OPERATE_TYPE_5, "import"),
+
+    /**
+     * 导出
+     */
+    EXPORT(CommonConstant.OPERATE_TYPE_6, "export");
+
+    /**
+     * 类型 1列表,2新增,3编辑,4删除,5导入,6导出
+     */
+    int type;
+
+    /**
+     * 编码(请求方式)
+     */
+    String code;
+
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    /**
+     * 构造器
+     *
+     * @param type 类型
+     * @param code 编码(请求方式)
+     */
+    OperateTypeEnum(int type, String code) {
+        this.type = type;
+        this.code = code;
+    }
+
+
+    /**
+     * 根据请求名称匹配
+     *
+     * @param methodName 请求名称
+     * @return Integer 类型
+     */
+    public static Integer getTypeByMethodName(String methodName) {
+        for (OperateTypeEnum e : OperateTypeEnum.values()) {
+            if (methodName.startsWith(e.getCode())) {
+                return e.getType();
+            }
+        }
+        return CommonConstant.OPERATE_TYPE_1;
+    }
+}

+ 97 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/RoleIndexConfigEnum.java

@@ -0,0 +1,97 @@
+package org.jeecg.common.constant.enums;
+
+import org.jeecg.common.util.oConvertUtils;
+
+import java.util.List;
+
+/**
+ * 首页自定义
+ * 通过角色编码与首页组件路径配置
+ * 枚举的顺序有权限高低权重作用(也就是配置多个角色,在前面的角色首页,会优先生效)
+ * @author: jeecg-boot
+ */
+public enum RoleIndexConfigEnum {
+
+    /**首页自定义 admin*/
+//    ADMIN("admin", "dashboard/Analysis"),
+    //TEST("test",  "dashboard/IndexChart"),
+    /**首页自定义 hr*/
+//    HR("hr", "dashboard/IndexBdc");
+  
+    //DM("dm", "dashboard/IndexTask"),
+
+    // 注:此值仅为防止报错,无任何实际意义
+    ROLE_INDEX_CONFIG_ENUM("RoleIndexConfigEnumDefault", "dashboard/Analysis");
+
+    /**
+     * 角色编码
+     */
+    String roleCode;
+    /**
+     * 路由index
+     */
+    String componentUrl;
+
+    /**
+     * 构造器
+     *
+     * @param roleCode 角色编码
+     * @param componentUrl 首页组件路径(规则跟菜单配置一样)
+     */
+    RoleIndexConfigEnum(String roleCode, String componentUrl) {
+        this.roleCode = roleCode;
+        this.componentUrl = componentUrl;
+    }
+    /**
+     * 根据code找枚举
+     * @param roleCode 角色编码
+     * @return
+     */
+    private static RoleIndexConfigEnum getEnumByCode(String roleCode) {
+        for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
+            if (e.roleCode.equals(roleCode)) {
+                return e;
+            }
+        }
+        return null;
+    }
+    /**
+     * 根据code找index
+     * @param roleCode 角色编码
+     * @return
+     */
+    private static String getIndexByCode(String roleCode) {
+        for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
+            if (e.roleCode.equals(roleCode)) {
+                return e.componentUrl;
+            }
+        }
+        return null;
+    }
+
+    public static String getIndexByRoles(List<String> roles) {
+        String[] rolesArray = roles.toArray(new String[roles.size()]);
+        for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
+            if (oConvertUtils.isIn(e.roleCode,rolesArray)){
+                return e.componentUrl;
+            }
+        }
+        return null;
+    }
+
+    public String getRoleCode() {
+        return roleCode;
+    }
+
+    public void setRoleCode(String roleCode) {
+        this.roleCode = roleCode;
+    }
+
+    public String getComponentUrl() {
+        return componentUrl;
+    }
+
+    public void setComponentUrl(String componentUrl) {
+        this.componentUrl = componentUrl;
+    }
+}

+ 82 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/SysAnnmentTypeEnum.java

@@ -0,0 +1,82 @@
+package org.jeecg.common.constant.enums;
+
+import org.jeecg.common.util.oConvertUtils;
+
+/**
+ * 系统公告自定义跳转方式
+ * @author: jeecg-boot
+ */
+public enum SysAnnmentTypeEnum {
+    /**
+     * 邮件跳转组件
+     */
+    EMAIL("email", "component", "modules/eoa/email/modals/EoaEmailInForm"),
+    /**
+     * 流程跳转到我的任务
+     */
+    BPM("bpm", "url", "/bpm/task/MyTaskList"),
+    
+    /**
+     * 流程抄送任务
+     */
+    BPM_VIEW("bpm_cc", "url", "/bpm/task/MyTaskList"),
+    /**
+     * 邀请用户跳转到个人设置
+     */
+    TENANT_INVITE("tenant_invite", "url", "/system/usersetting");
+
+    /**
+     * 业务类型(email:邮件 bpm:流程)
+     */
+    private String type;
+    /**
+     * 打开方式 组件:component 路由:url
+     */
+    private String openType;
+    /**
+     * 组件/路由 地址
+     */
+    private String openPage;
+
+    SysAnnmentTypeEnum(String type, String openType, String openPage) {
+        this.type = type;
+        this.openType = openType;
+        this.openPage = openPage;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getOpenType() {
+        return openType;
+    }
+
+    public void setOpenType(String openType) {
+        this.openType = openType;
+    }
+
+    public String getOpenPage() {
+        return openPage;
+    }
+
+    public void setOpenPage(String openPage) {
+        this.openPage = openPage;
+    }
+
+    public static SysAnnmentTypeEnum getByType(String type) {
+        if (oConvertUtils.isEmpty(type)) {
+            return null;
+        }
+        for (SysAnnmentTypeEnum val : values()) {
+            if (val.getType().equals(type)) {
+                return val;
+            }
+        }
+        return null;
+    }
+}

+ 75 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/Vue3MessageHrefEnum.java

@@ -0,0 +1,75 @@
+package org.jeecg.common.constant.enums;
+
+import org.jeecg.common.system.annotation.EnumDict;
+import org.jeecg.common.system.vo.DictModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 消息跳转【vue3】
+ * @Author taoYan
+ * @Date 2022/8/19 20:41
+ **/
+@EnumDict("messageHref")
+public enum Vue3MessageHrefEnum {
+
+    /**
+     * 流程催办
+     */
+    BPM("bpm", "/task/myHandleTaskInfo"),
+    
+    /**
+     * 系统消息通知
+     */
+    BPM_SYSTEM_MSG("bpm_msg_node", ""),
+    
+    /**
+     * 流程抄送任务
+     */
+    BPM_VIEW("bpm_cc", "/task/myHandleTaskInfo"),
+
+    /**
+     * 节点通知
+     */
+    BPM_TASK("bpm_task", "/task/myHandleTaskInfo"),
+
+    /**
+     * 邮件消息
+     */
+    EMAIL("email", "/eoa/email");
+    
+    String busType;
+    
+    String path;
+
+    Vue3MessageHrefEnum(String busType, String path) {
+        this.busType = busType;
+        this.path = path;
+    }
+
+    public String getBusType() {
+        return busType;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * 获取字典数据
+     * @return
+     */
+    public static List<DictModel> getDictList(){
+        List<DictModel> list = new ArrayList<>();
+        DictModel dictModel = null;
+        for(Vue3MessageHrefEnum e: Vue3MessageHrefEnum.values()){
+            dictModel = new DictModel();
+            dictModel.setValue(e.getBusType());
+            dictModel.setText(e.getPath());
+            list.add(dictModel);
+        }
+        return list;
+    }
+    
+}

+ 20 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveDecode.java

@@ -0,0 +1,20 @@
+package org.jeecg.common.desensitization.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 解密注解
+ *
+ * 在方法上定义 将方法返回对象中的敏感字段 解密,需要注意的是,如果没有加密过,解密会出问题,返回原字符串
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface SensitiveDecode {
+
+    /**
+     * 指明需要脱敏的实体类class
+     * @return
+     */
+    Class entity() default Object.class;
+}

+ 20 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveEncode.java

@@ -0,0 +1,20 @@
+package org.jeecg.common.desensitization.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 加密注解
+ *
+ * 在方法上声明 将方法返回对象中的敏感字段 加密/格式化
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface SensitiveEncode {
+
+    /**
+     * 指明需要脱敏的实体类class
+     * @return
+     */
+    Class entity() default Object.class;
+}

+ 21 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveField.java

@@ -0,0 +1,21 @@
+package org.jeecg.common.desensitization.annotation;
+
+
+import org.jeecg.common.desensitization.enums.SensitiveEnum;
+
+import java.lang.annotation.*;
+
+/**
+ * 在字段上定义 标识字段存储的信息是敏感的
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface SensitiveField {
+
+    /**
+     * 不同类型处理不同
+     * @return
+     */
+    SensitiveEnum type() default SensitiveEnum.ENCODE;
+}

+ 81 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/aspect/SensitiveDataAspect.java

@@ -0,0 +1,81 @@
+package org.jeecg.common.desensitization.aspect;
+
+import lombok.extern.slf4j.Slf4j;
+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.jeecg.common.desensitization.annotation.SensitiveDecode;
+import org.jeecg.common.desensitization.annotation.SensitiveEncode;
+import org.jeecg.common.desensitization.util.SensitiveInfoUtil;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * 敏感数据切面处理类
+ * @Author taoYan
+ * @Date 2022/4/20 17:45
+ **/
+@Slf4j
+@Aspect
+@Component
+public class SensitiveDataAspect {
+
+    /**
+     * 定义切点Pointcut
+     */
+    @Pointcut("@annotation(org.jeecg.common.desensitization.annotation.SensitiveEncode) || @annotation(org.jeecg.common.desensitization.annotation.SensitiveDecode)")
+    public void sensitivePointCut() {
+    }
+
+    @Around("sensitivePointCut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        // 处理结果
+        Object result = point.proceed();
+        if(result == null){
+            return result;
+        }
+        Class resultClass = result.getClass();
+        log.debug(" resultClass  = {}" , resultClass);
+
+        if(resultClass.isPrimitive()){
+            //是基本类型 直接返回 不需要处理
+            return result;
+        }
+        // 获取方法注解信息:是哪个实体、是加密还是解密
+        boolean isEncode = true;
+        Class entity = null;
+        MethodSignature methodSignature = (MethodSignature) point.getSignature();
+        Method method = methodSignature.getMethod();
+        SensitiveEncode encode = method.getAnnotation(SensitiveEncode.class);
+        if(encode==null){
+            SensitiveDecode decode = method.getAnnotation(SensitiveDecode.class);
+            if(decode!=null){
+                entity = decode.entity();
+                isEncode = false;
+            }
+        }else{
+            entity = encode.entity();
+        }
+
+        long startTime=System.currentTimeMillis();
+        if(resultClass.equals(entity) || entity.equals(Object.class)){
+            // 方法返回实体和注解的entity一样,如果注解没有申明entity属性则认为是(方法返回实体和注解的entity一样)
+            SensitiveInfoUtil.handlerObject(result, isEncode);
+        } else if(result instanceof List){
+            // 方法返回List<实体>
+            SensitiveInfoUtil.handleList(result, entity, isEncode);
+        }else{
+            // 方法返回一个对象
+            SensitiveInfoUtil.handleNestedObject(result, entity, isEncode);
+        }
+        long endTime=System.currentTimeMillis();
+        log.debug((isEncode ? "加密操作," : "解密操作,") + "Aspect程序耗时:" + (endTime - startTime) + "ms");
+
+        return result;
+    }
+
+}

+ 55 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/enums/SensitiveEnum.java

@@ -0,0 +1,55 @@
+package org.jeecg.common.desensitization.enums;
+
+/**
+ * 敏感字段信息类型
+ */
+public enum SensitiveEnum {
+
+
+    /**
+     * 加密
+     */
+    ENCODE,
+
+    /**
+     * 中文名
+     */
+    CHINESE_NAME,
+
+    /**
+     * 身份证号
+     */
+    ID_CARD,
+
+    /**
+     * 座机号
+     */
+    FIXED_PHONE,
+
+    /**
+     * 手机号
+     */
+    MOBILE_PHONE,
+
+    /**
+     * 地址
+     */
+    ADDRESS,
+
+    /**
+     * 电子邮件
+     */
+    EMAIL,
+
+    /**
+     * 银行卡
+     */
+    BANK_CARD,
+
+    /**
+     * 公司开户银行联号
+     */
+    CNAPS_CODE;
+
+
+}

+ 363 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/util/SensitiveInfoUtil.java

@@ -0,0 +1,363 @@
+package org.jeecg.common.desensitization.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.desensitization.annotation.SensitiveField;
+import org.jeecg.common.desensitization.enums.SensitiveEnum;
+import org.jeecg.common.util.encryption.AesEncryptUtil;
+import org.jeecg.common.util.oConvertUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 敏感信息处理工具类
+ * @author taoYan
+ * @date 2022/4/20 18:01
+ **/
+@Slf4j
+public class SensitiveInfoUtil {
+
+    /**
+     * 处理嵌套对象
+     * @param obj 方法返回值
+     * @param entity 实体class
+     * @param isEncode 是否加密(true: 加密操作 / false:解密操作)
+     * @throws IllegalAccessException
+     */
+    public static void handleNestedObject(Object obj, Class entity, boolean isEncode) throws IllegalAccessException {
+        Field[] fields = obj.getClass().getDeclaredFields();
+        for (Field field : fields) {
+            if(field.getType().isPrimitive()){
+                continue;
+            }
+            if(field.getType().equals(entity)){
+                // 对象里面是实体
+                field.setAccessible(true);
+                Object nestedObject = field.get(obj);
+                handlerObject(nestedObject, isEncode);
+                break;
+            }else{
+                // 对象里面是List<实体>
+                if(field.getGenericType() instanceof ParameterizedType){
+                    ParameterizedType pt = (ParameterizedType)field.getGenericType();
+                    if(pt.getRawType().equals(List.class)){
+                        if(pt.getActualTypeArguments()[0].equals(entity)){
+                            field.setAccessible(true);
+                            Object nestedObject = field.get(obj);
+                            handleList(nestedObject, entity, isEncode);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 处理Object
+     * @param obj 方法返回值
+     * @param isEncode 是否加密(true: 加密操作 / false:解密操作)
+     * @return
+     * @throws IllegalAccessException
+     */
+    public static Object handlerObject(Object obj, boolean isEncode) throws IllegalAccessException {
+        if (oConvertUtils.isEmpty(obj)) {
+            return obj;
+        }
+        long startTime=System.currentTimeMillis();
+        log.debug(" obj --> "+ obj.toString());
+
+        // 判断是不是一个对象
+        Field[] fields = obj.getClass().getDeclaredFields();
+        for (Field field : fields) {
+            boolean isSensitiveField = field.isAnnotationPresent(SensitiveField.class);
+            if(isSensitiveField){
+                // 必须有SensitiveField注解 才作处理
+                if(field.getType().isAssignableFrom(String.class)){
+                    //必须是字符串类型 才作处理
+                    field.setAccessible(true);
+                    String realValue = (String) field.get(obj);
+                    if(realValue==null || "".equals(realValue)){
+                        continue;
+                    }
+                    SensitiveField sf = field.getAnnotation(SensitiveField.class);
+                    if(isEncode==true){
+                        //加密
+                        String value = SensitiveInfoUtil.getEncodeData(realValue,  sf.type());
+                        field.set(obj, value);
+                    }else{
+                        //解密只处理 encode类型的
+                        if(sf.type().equals(SensitiveEnum.ENCODE)){
+                            String value = SensitiveInfoUtil.getDecodeData(realValue);
+                            field.set(obj, value);
+                        }
+                    }
+                }
+            }
+        }
+        long endTime=System.currentTimeMillis();
+        log.debug((isEncode ? "加密操作," : "解密操作,") + "当前程序耗时:" + (endTime - startTime) + "ms");
+        return obj;
+    }
+
+    /**
+     * 处理 List<实体>
+     * @param obj
+     * @param entity
+     * @param isEncode(true: 加密操作 / false:解密操作)
+     */
+    public static void handleList(Object obj, Class entity, boolean isEncode){
+        List list = (List)obj;
+        if(list.size()>0){
+            Object first = list.get(0);
+            if(first.getClass().equals(entity)){
+                for(int i=0; i<list.size(); i++){
+                    Object temp = list.get(i);
+                    try {
+                        handlerObject(temp, isEncode);
+                    } catch (IllegalAccessException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 处理数据 获取解密后的数据
+     * @param data
+     * @return
+     */
+    public static String getDecodeData(String data){
+        String result = null;
+        try {
+            result = AesEncryptUtil.desEncrypt(data);
+        } catch (Exception exception) {
+            log.debug("数据解密错误,原数据:"+data);
+        }
+        //解决debug模式下,加解密失效导致中文被解密变成空的问题
+        if(oConvertUtils.isEmpty(result) && oConvertUtils.isNotEmpty(data)){
+            result = data;
+        }
+        return result;
+    }
+
+    /**
+     * 处理数据 获取加密后的数据 或是格式化后的数据
+     * @param data 字符串
+     * @param sensitiveEnum 类型
+     * @return 处理后的字符串
+     */
+    public static String getEncodeData(String data, SensitiveEnum sensitiveEnum){
+        String result;
+        switch (sensitiveEnum){
+            case ENCODE:
+                try {
+                    result = AesEncryptUtil.encrypt(data);
+                } catch (Exception exception) {
+                    log.error("数据加密错误", exception.getMessage());
+                    result = data;
+                }
+                break;
+            case CHINESE_NAME:
+                result = chineseName(data);
+                break;
+            case ID_CARD:
+                result = idCardNum(data);
+                break;
+            case FIXED_PHONE:
+                result = fixedPhone(data);
+                break;
+            case MOBILE_PHONE:
+                result = mobilePhone(data);
+                break;
+            case ADDRESS:
+                result = address(data, 3);
+                break;
+            case EMAIL:
+                result = email(data);
+                break;
+            case BANK_CARD:
+                result = bankCard(data);
+                break;
+            case CNAPS_CODE:
+                result = cnapsCode(data);
+                break;
+            default:
+                result = data;
+        }
+        return result;
+    }
+
+
+    /**
+     * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号
+     * @param fullName 全名
+     * @return <例子:李**>
+     */
+    private static String chineseName(String fullName) {
+        if (oConvertUtils.isEmpty(fullName)) {
+            return "";
+        }
+        return formatRight(fullName, 1);
+    }
+
+    /**
+     * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号
+     * @param familyName 姓
+     * @param firstName 名
+     * @return <例子:李**>
+     */
+    private static String chineseName(String familyName, String firstName) {
+        if (oConvertUtils.isEmpty(familyName) || oConvertUtils.isEmpty(firstName)) {
+            return "";
+        }
+        return chineseName(familyName + firstName);
+    }
+
+    /**
+     * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。
+     * @param id 身份证号
+     * @return <例子:*************5762>
+     */
+    private static String idCardNum(String id) {
+        if (oConvertUtils.isEmpty(id)) {
+            return "";
+        }
+        return formatLeft(id, 4);
+
+    }
+
+    /**
+     * [固定电话] 后四位,其他隐藏
+     * @param num 固定电话
+     * @return <例子:****1234>
+     */
+    private static String fixedPhone(String num) {
+        if (oConvertUtils.isEmpty(num)) {
+            return "";
+        }
+        return formatLeft(num, 4);
+    }
+
+    /**
+     * [手机号码] 前三位,后四位,其他隐藏
+     * @param num 手机号码
+     * @return <例子:138******1234>
+     */
+    private static String mobilePhone(String num) {
+        if (oConvertUtils.isEmpty(num)) {
+            return "";
+        }
+        int len = num.length();
+        if(len<11){
+            return num;
+        }
+        return formatBetween(num, 3, 4);
+    }
+
+    /**
+     * [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护
+     * @param address 地址
+     * @param sensitiveSize 敏感信息长度
+     * @return <例子:北京市海淀区****>
+     */
+    private static String address(String address, int sensitiveSize) {
+        if (oConvertUtils.isEmpty(address)) {
+            return "";
+        }
+        int len = address.length();
+        if(len<sensitiveSize){
+            return address;
+        }
+        return formatRight(address, sensitiveSize);
+    }
+
+    /**
+     * [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示
+     * @param email 电子邮箱
+     * @return <例子:g**@163.com>
+     */
+    private static String email(String email) {
+        if (oConvertUtils.isEmpty(email)) {
+            return "";
+        }
+        int index = email.indexOf("@");
+        if (index <= 1){
+            return email;
+        }
+        String begin = email.substring(0, 1);
+        String end = email.substring(index);
+        String stars = "**";
+        return begin + stars + end;
+    }
+
+    /**
+     * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号
+     * @param cardNum 银行卡号
+     * @return <例子:6222600**********1234>
+     */
+    private static String bankCard(String cardNum) {
+        if (oConvertUtils.isEmpty(cardNum)) {
+            return "";
+        }
+        return formatBetween(cardNum, 6, 4);
+    }
+
+    /**
+     * [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号
+     * @param code 公司开户银行联号
+     * @return <例子:12********>
+     */
+    private static String cnapsCode(String code) {
+        if (oConvertUtils.isEmpty(code)) {
+            return "";
+        }
+        return formatRight(code, 2);
+    }
+
+
+    /**
+     * 将右边的格式化成*
+     * @param str 字符串
+     * @param reservedLength 保留长度
+     * @return 格式化后的字符串
+     */
+    private static String formatRight(String str, int reservedLength){
+        String name = str.substring(0, reservedLength);
+        String stars = String.join("", Collections.nCopies(str.length()-reservedLength, "*"));
+        return name + stars;
+    }
+
+    /**
+     * 将左边的格式化成*
+     * @param str 字符串
+     * @param reservedLength 保留长度
+     * @return 格式化后的字符串
+     */
+    private static String formatLeft(String str, int reservedLength){
+        int len = str.length();
+        String show = str.substring(len-reservedLength);
+        String stars = String.join("", Collections.nCopies(len-reservedLength, "*"));
+        return stars + show;
+    }
+
+    /**
+     * 将中间的格式化成*
+     * @param str 字符串
+     * @param beginLen 开始保留长度
+     * @param endLen 结尾保留长度
+     * @return 格式化后的字符串
+     */
+    private static String formatBetween(String str, int beginLen, int endLen){
+        int len = str.length();
+        String begin = str.substring(0, beginLen);
+        String end = str.substring(len-endLen);
+        String stars = String.join("", Collections.nCopies(len-beginLen-endLen, "*"));
+        return begin + stars + end;
+    }
+
+}

+ 37 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/BisonException.java

@@ -0,0 +1,37 @@
+package org.jeecg.common.exception;
+
+import org.jeecg.common.constant.CommonConstant;
+
+/**
+ * bison业务异常
+ *
+ * @author sunweidong
+ * @since 2024/8/27
+ */
+public class BisonException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    private int errCode = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
+
+    public BisonException(String message) {
+        super(message);
+    }
+
+    public BisonException(String message, int errCode) {
+        super(message);
+        this.errCode = errCode;
+    }
+
+    public int getErrCode() {
+        return errCode;
+    }
+
+    public BisonException(Throwable cause) {
+        super(cause);
+    }
+
+    public BisonException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 37 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/BisonU8Exception.java

@@ -0,0 +1,37 @@
+package org.jeecg.common.exception;
+
+import org.jeecg.common.constant.CommonConstant;
+
+/**
+ * bison与用友交互业务异常
+ *
+ * @author sunweidong
+ * @since 2024/8/27
+ */
+public class BisonU8Exception extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    private int errCode = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
+
+    public BisonU8Exception(String message) {
+        super(message);
+    }
+
+    public BisonU8Exception(String message, int errCode) {
+        super(message);
+        this.errCode = errCode;
+    }
+
+    public int getErrCode() {
+        return errCode;
+    }
+
+    public BisonU8Exception(Throwable cause) {
+        super(cause);
+    }
+
+    public BisonU8Exception(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 23 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBoot401Exception.java

@@ -0,0 +1,23 @@
+package org.jeecg.common.exception;
+
+/**
+ * @Description: jeecg-boot自定义401异常
+ * @author: jeecg-boot
+ */
+public class JeecgBoot401Exception extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+	public JeecgBoot401Exception(String message){
+		super(message);
+	}
+
+	public JeecgBoot401Exception(Throwable cause)
+	{
+		super(cause);
+	}
+
+	public JeecgBoot401Exception(String message, Throwable cause)
+	{
+		super(message,cause);
+	}
+}

+ 39 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootException.java

@@ -0,0 +1,39 @@
+package org.jeecg.common.exception;
+
+import org.jeecg.common.constant.CommonConstant;
+
+/**
+ * @Description: jeecg-boot自定义异常
+ * @author: jeecg-boot
+ */
+public class JeecgBootException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 返回给前端的错误code
+	 */
+	private int errCode = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
+
+	public JeecgBootException(String message){
+		super(message);
+	}
+
+	public JeecgBootException(String message, int errCode){
+		super(message);
+		this.errCode = errCode;
+	}
+
+	public int getErrCode() {
+		return errCode;
+	}
+
+	public JeecgBootException(Throwable cause)
+	{
+		super(cause);
+	}
+	
+	public JeecgBootException(String message,Throwable cause)
+	{
+		super(message,cause);
+	}
+}

+ 218 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java

@@ -0,0 +1,218 @@
+package org.jeecg.common.exception;
+
+import cn.hutool.core.util.ObjectUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.UnauthorizedException;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.enums.SentinelErrorInfoEnum;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.data.redis.connection.PoolException;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+import org.springframework.web.servlet.NoHandlerFoundException;
+
+import java.util.List;
+
+/**
+ * 异常处理器
+ *
+ * @Author scott
+ * @Date 2019
+ */
+@RestControllerAdvice
+@Slf4j
+public class JeecgBootExceptionHandler {
+
+    /**
+     * 处理自定义异常
+     */
+    @ExceptionHandler(JeecgBootException.class)
+    public Result<?> handleJeecgBootException(JeecgBootException e) {
+        log.error(e.getMessage(), e);
+        return Result.error(e.getErrCode(), e.getMessage());
+    }
+
+    /**
+     * 处理自定义异常
+     */
+    @ExceptionHandler(BisonException.class)
+    public Result<?> handleBisonException(BisonException e) {
+        log.warn(e.getMessage());
+        return Result.error(e.getMessage());
+    }
+
+    /**
+     * 处理自定义异常
+     */
+    @ExceptionHandler(BisonU8Exception.class)
+    public Result<?> handleBisonU8Exception(BisonU8Exception e) {
+        log.warn("", e);
+        return Result.error(e.getMessage());
+    }
+
+    /**
+     * 处理自定义微服务异常
+     */
+    @ExceptionHandler(JeecgCloudException.class)
+    public Result<?> handleJeecgCloudException(JeecgCloudException e) {
+        log.error(e.getMessage(), e);
+        return Result.error(e.getMessage());
+    }
+
+    /**
+     * 处理自定义微服务异常
+     */
+    @ExceptionHandler(MissingServletRequestParameterException.class)
+    public Result<?> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
+        log.warn(e.getMessage());
+        return Result.error("操作失败,请求参数[ " + e.getParameterName() + " ]不能为空");
+    }
+
+    /**
+     * 处理自定义异常
+     */
+    @ExceptionHandler(JeecgBoot401Exception.class)
+    @ResponseStatus(HttpStatus.UNAUTHORIZED)
+    public Result<?> handleJeecgBoot401Exception(JeecgBoot401Exception e) {
+        log.error(e.getMessage(), e);
+        return new Result(401, e.getMessage());
+    }
+
+    @ExceptionHandler(NoHandlerFoundException.class)
+    public Result<?> handlerNoFoundException(Exception e) {
+        log.error(e.getMessage(), e);
+        return Result.error(404, "路径不存在,请检查路径是否正确");
+    }
+
+    @ExceptionHandler(DuplicateKeyException.class)
+    public Result<?> handleDuplicateKeyException(DuplicateKeyException e) {
+        log.error(e.getMessage(), e);
+        return Result.error("数据库中已存在该记录");
+    }
+
+    @ExceptionHandler({UnauthorizedException.class, AuthorizationException.class})
+    public Result<?> handleAuthorizationException(AuthorizationException e) {
+        String message = e.getMessage();
+        if (message.startsWith("Subject does not have role [")) {
+            String replace = message.replace("Subject does not have role [", "").replace("]", "");
+            return Result.noauth("没有名为[" + replace + "]的角色权限,请联系管理员授权");
+        }
+        if (message.startsWith("Subject does not have permission [")) {
+            String replace = message.replace("Subject does not have role [", "").replace("]", "");
+            return Result.noauth("没有名为[" + replace + "]的操作权限,请联系管理员授权");
+        }
+        log.error(message, e);
+        return Result.noauth("没有权限,请联系管理员授权");
+    }
+
+    @ExceptionHandler(Exception.class)
+    public Result<?> handleException(Exception e) {
+        log.error(e.getMessage(), e);
+        // update-begin---author:zyf ---date:20220411  for:处理Sentinel限流自定义异常
+        Throwable throwable = e.getCause();
+        SentinelErrorInfoEnum errorInfoEnum = SentinelErrorInfoEnum.getErrorByException(throwable);
+        if (ObjectUtil.isNotEmpty(errorInfoEnum)) {
+            return Result.error(errorInfoEnum.getError());
+        }
+        // update-end---author:zyf ---date:20220411  for:处理Sentinel限流自定义异常
+        return Result.error(e.getMessage());
+    }
+
+    /**
+     * @param e
+     * @return
+     * @Author 政辉
+     */
+    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+    public Result<?> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("不支持");
+        sb.append(e.getMethod());
+        sb.append("请求方法,");
+        sb.append("支持以下");
+        String[] methods = e.getSupportedMethods();
+        if (methods != null) {
+            for (String str : methods) {
+                sb.append(str);
+                sb.append("、");
+            }
+        }
+        log.error(sb.toString(), e);
+        // return Result.error("没有权限,请联系管理员授权");
+        return Result.error(405, sb.toString());
+    }
+
+    /**
+     * spring默认上传大小100MB 超出大小捕获异常MaxUploadSizeExceededException
+     */
+    @ExceptionHandler(MaxUploadSizeExceededException.class)
+    public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
+        log.error(e.getMessage(), e);
+        return Result.error("文件大小超出10MB限制, 请压缩或降低文件质量! ");
+    }
+
+    @ExceptionHandler(DataIntegrityViolationException.class)
+    public Result<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
+        log.error(e.getMessage(), e);
+        //【issues/3624】数据库执行异常handleDataIntegrityViolationException提示有误 #3624
+        return Result.error("执行数据库异常,违反了完整性例如:违反惟一约束、违反非空限制、字段内容超出长度等");
+    }
+
+    @ExceptionHandler(PoolException.class)
+    public Result<?> handlePoolException(PoolException e) {
+        log.error(e.getMessage(), e);
+        return Result.error("Redis 连接异常!");
+    }
+
+
+    /**
+     * SQL注入风险,全局异常处理
+     *
+     * @param exception
+     * @return
+     */
+    @ExceptionHandler(JeecgSqlInjectionException.class)
+    public Result<?> handleSQLException(Exception exception) {
+        String msg = exception.getMessage().toLowerCase();
+        final String extractvalue = "extractvalue";
+        final String updatexml = "updatexml";
+        boolean hasSensitiveInformation = msg.indexOf(extractvalue) >= 0 || msg.indexOf(updatexml) >= 0;
+        if (msg != null && hasSensitiveInformation) {
+            log.error("校验失败,存在SQL注入风险!{}", msg);
+            return Result.error("校验失败,存在SQL注入风险!");
+        }
+        return Result.error("校验失败,存在SQL注入风险!" + msg);
+    }
+
+    @ExceptionHandler(BindException.class)
+    public Result handleMethodArgumentNotValidException(BindException e) {
+        BindingResult result = e.getBindingResult();
+        StringBuilder stringBuilder = new StringBuilder();
+        if (result.hasErrors()) {
+            List<ObjectError> errors = result.getAllErrors();
+            if (errors != null) {
+                errors.forEach(p -> {
+                    FieldError fieldError = (FieldError) p;
+                    log.warn("Bad Request Parameters: dto entity [{}],field [{}],message [{}]",
+                            fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage());
+                    stringBuilder.append(fieldError.getDefaultMessage()).append(",");
+                });
+            }
+            if (stringBuilder.length() > 0) {
+                stringBuilder.deleteCharAt(stringBuilder.length() - 1);
+            }
+        }
+        return Result.error(stringBuilder.toString());
+    }
+}

+ 23 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgSqlInjectionException.java

@@ -0,0 +1,23 @@
+package org.jeecg.common.exception;
+
+/**
+ * @Description: jeecg-boot自定义SQL注入异常
+ * @author: jeecg-boot
+ */
+public class JeecgSqlInjectionException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+	public JeecgSqlInjectionException(String message){
+		super(message);
+	}
+	
+	public JeecgSqlInjectionException(Throwable cause)
+	{
+		super(cause);
+	}
+	
+	public JeecgSqlInjectionException(String message, Throwable cause)
+	{
+		super(message,cause);
+	}
+}

+ 22 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/handler/IFillRuleHandler.java

@@ -0,0 +1,22 @@
+package org.jeecg.common.handler;
+
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * 填值规则接口
+ *
+ * @author Yan_东
+ * 如需使用填值规则功能,规则实现类必须实现此接口
+ */
+public interface IFillRuleHandler {
+
+    /**
+     * 填值规则
+     * @param params 页面配置固定参数
+     * @param formData  动态表单参数
+     * @return
+     */
+    public Object execute(JSONObject params, JSONObject formData);
+
+}
+

+ 19 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/annotation/EnumDict.java

@@ -0,0 +1,19 @@
+package org.jeecg.common.system.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 将枚举类转化成字典数据
+ * @Author taoYan
+ * @Date 2022/7/8 10:34
+ **/
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface EnumDict {
+
+    /**
+     * 作为字典数据的唯一编码
+     */
+    String value() default "";
+}

+ 55 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java

@@ -0,0 +1,55 @@
+package org.jeecg.common.system.base.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.config.JeecgBaseConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.List;
+
+@Slf4j
+public class JeecgController<T, S extends IService<T>> {
+    @Autowired
+    protected S service;
+    @Resource
+    private JeecgBaseConfig jeecgBaseConfig;
+
+    protected List<T> getExportList(HttpServletRequest request, T object) {
+        // Step.1 组装查询条件
+        QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
+        // 过滤选中数据
+        String selections = request.getParameter("selections");
+        if (oConvertUtils.isNotEmpty(selections)) {
+            List<String> selectionList = Arrays.asList(selections.split(","));
+            queryWrapper.in("id", selectionList);
+        }
+        // Step.2 获取导出数据
+        return service.list(queryWrapper);
+    }
+
+    private String getId(T item) {
+        try {
+            return PropertyUtils.getProperty(item, "id").toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    protected Object doImport(List<T> list, String filePath) {
+        // 具体的导入逻辑
+        return null;
+    }
+
+    protected void execExportRecord(List<T> list) {
+
+    }
+
+}

+ 61 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/entity/JeecgEntity.java

@@ -0,0 +1,61 @@
+package org.jeecg.common.system.base.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+
+/**
+ * @Description: Entity基类
+ * @Author: dangzhenghui@163.com
+ * @Date: 2019-4-28
+ * @Version: 1.1
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class JeecgEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    @ApiModelProperty(value = "ID")
+    private java.lang.String id;
+
+    /**
+     * 创建人
+     */
+    @ApiModelProperty(value = "创建人")
+    private java.lang.String createBy;
+
+    /**
+     * 创建时间
+     */
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private java.util.Date createTime;
+
+    /**
+     * 更新人
+     */
+    @ApiModelProperty(value = "更新人")
+    private java.lang.String updateBy;
+
+    /**
+     * 更新时间
+     */
+    @ApiModelProperty(value = "更新时间")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private java.util.Date updateTime;
+
+}

+ 12 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/JeecgService.java

@@ -0,0 +1,12 @@
+package org.jeecg.common.system.base.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * @Description: Service基类
+ * @Author: dangzhenghui@163.com
+ * @Date: 2019-4-21 8:13
+ * @Version: 1.0
+ */
+public interface JeecgService<T> extends IService<T> {
+}

+ 18 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/impl/JeecgServiceImpl.java

@@ -0,0 +1,18 @@
+package org.jeecg.common.system.base.service.impl;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.system.base.entity.JeecgEntity;
+import org.jeecg.common.system.base.service.JeecgService;
+
+/**
+ * @Description: ServiceImpl基类
+ * @Author: dangzhenghui@163.com
+ * @Date: 2019-4-21 8:13
+ * @Version: 1.0
+ */
+@Slf4j
+public class JeecgServiceImpl<M extends BaseMapper<T>, T extends JeecgEntity> extends ServiceImpl<M, T> implements JeecgService<T> {
+
+}

+ 45 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/MatchTypeEnum.java

@@ -0,0 +1,45 @@
+package org.jeecg.common.system.query;
+
+import org.jeecg.common.util.oConvertUtils;
+
+/**
+ * 查询链接规则
+ *
+ * @Author Sunjianlei
+ */
+public enum MatchTypeEnum {
+
+    /**查询链接规则 AND*/
+    AND("AND"),
+    /**查询链接规则 OR*/
+    OR("OR");
+
+    private String value;
+
+    MatchTypeEnum(String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public static MatchTypeEnum getByValue(Object value) {
+        if (oConvertUtils.isEmpty(value)) {
+            return null;
+        }
+        return getByValue(value.toString());
+    }
+
+    public static MatchTypeEnum getByValue(String value) {
+        if (oConvertUtils.isEmpty(value)) {
+            return null;
+        }
+        for (MatchTypeEnum val : values()) {
+            if (val.getValue().toLowerCase().equals(value.toLowerCase())) {
+                return val;
+            }
+        }
+        return null;
+    }
+}

+ 81 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryCondition.java

@@ -0,0 +1,81 @@
+package org.jeecg.common.system.query;
+
+import java.io.Serializable;
+
+/**
+ * @Description: QueryCondition
+ * @author: jeecg-boot
+ */
+public class QueryCondition implements Serializable {
+
+	private static final long serialVersionUID = 4740166316629191651L;
+	
+	private String field;
+	/** 组件的类型(例如:input、select、radio) */
+	private String type;
+	/**
+	 * 对应的数据库字段的类型
+	 * 支持:int、bigDecimal、short、long、float、double、boolean
+	 */
+	private String dbType;
+	private String rule;
+	private String val;
+
+	public QueryCondition(String field, String type, String dbType, String rule, String val) {
+		this.field = field;
+		this.type = type;
+		this.dbType = dbType;
+		this.rule = rule;
+		this.val = val;
+	}
+	
+	public String getField() {
+		return field;
+	}
+
+	public void setField(String field) {
+		this.field = field;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public String getDbType() {
+		return dbType;
+	}
+
+	public void setDbType(String dbType) {
+		this.dbType = dbType;
+	}
+
+	public String getRule() {
+		return rule;
+	}
+
+	public void setRule(String rule) {
+		this.rule = rule;
+	}
+
+	public String getVal() {
+		return val;
+	}
+
+	public void setVal(String val) {
+		this.val = val;
+	}
+
+	@Override
+	public String toString(){
+		StringBuffer sb =new StringBuffer();
+		if(field == null || "".equals(field)){
+			return "";
+		}
+		sb.append(this.field).append(" ").append(this.rule).append(" ").append(this.type).append(" ").append(this.dbType).append(" ").append(this.val);
+		return sb.toString();
+	}
+}

+ 931 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java

@@ -0,0 +1,931 @@
+package org.jeecg.common.system.query;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.DataBaseConstant;
+import org.jeecg.common.exception.JeecgBootException;
+import org.jeecg.common.system.util.JeecgDataAutorUtils;
+import org.jeecg.common.system.util.JwtUtil;
+import org.jeecg.common.system.util.SqlConcatUtil;
+import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
+import org.jeecg.common.util.*;
+import org.springframework.util.NumberUtils;
+
+import java.beans.PropertyDescriptor;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.net.URLDecoder;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 查询生成器
+ * @author: jeecg-boot
+ */
+@Slf4j
+public class QueryGenerator {
+	public static final String SQL_RULES_COLUMN = "SQL_RULES_COLUMN";
+
+	private static final String BEGIN = "_begin";
+	private static final String END = "_end";
+	/**
+	 * 数字类型字段,拼接此后缀 接受多值参数
+	 */
+	private static final String MULTI = "_MultiString";
+	private static final String STAR = "*";
+	private static final String COMMA = ",";
+	/**
+	 * 查询 逗号转义符 相当于一个逗号【作废】
+	 */
+	public static final String QUERY_COMMA_ESCAPE = "++";
+	private static final String NOT_EQUAL = "!";
+	/**页面带有规则值查询,空格作为分隔符*/
+	private static final String QUERY_SEPARATE_KEYWORD = " ";
+	/**高级查询前端传来的参数名*/
+	private static final String SUPER_QUERY_PARAMS = "superQueryParams";
+	/** 高级查询前端传来的拼接方式参数名 */
+	private static final String SUPER_QUERY_MATCH_TYPE = "superQueryMatchType";
+	/** 单引号 */
+	public static final String SQL_SQ = "'";
+	/**排序列*/
+	public static final String ORDER_COLUMN = "column";
+	/**排序方式*/
+	public static final String ORDER_TYPE = "order";
+	private static final String ORDER_TYPE_ASC = "ASC";
+
+	/**mysql 模糊查询之特殊字符下划线 (_、\)*/
+	public static final String LIKE_MYSQL_SPECIAL_STRS = "_,%";
+
+	/**日期格式化yyyy-MM-dd*/
+	public static final String YYYY_MM_DD = "yyyy-MM-dd";
+
+	/**to_date*/
+	public static final String TO_DATE = "to_date";
+
+	/**时间格式化 */
+	private static final ThreadLocal<SimpleDateFormat> LOCAL = new ThreadLocal<SimpleDateFormat>();
+	private static SimpleDateFormat getTime(){
+		SimpleDateFormat time = LOCAL.get();
+		if(time == null){
+			time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            LOCAL.set(time);
+		}
+		return time;
+	}
+	
+	/**
+	 * 获取查询条件构造器QueryWrapper实例 通用查询条件已被封装完成
+	 * @param searchObj 查询实体
+	 * @param parameterMap request.getParameterMap()
+	 * @return QueryWrapper实例
+	 */
+	public static <T> QueryWrapper<T> initQueryWrapper(T searchObj,Map<String, String[]> parameterMap){
+		long start = System.currentTimeMillis();
+		QueryWrapper<T> queryWrapper = new QueryWrapper<T>();
+		installMplus(queryWrapper, searchObj, parameterMap);
+		log.debug("---查询条件构造器初始化完成,耗时:"+(System.currentTimeMillis()-start)+"毫秒----");
+		return queryWrapper;
+	}
+
+	/**
+	 * 组装Mybatis Plus 查询条件
+	 * <p>使用此方法 需要有如下几点注意:   
+	 * <br>1.使用QueryWrapper 而非LambdaQueryWrapper;
+	 * <br>2.实例化QueryWrapper时不可将实体传入参数   
+	 * <br>错误示例:如QueryWrapper<JeecgDemo> queryWrapper = new QueryWrapper<JeecgDemo>(jeecgDemo);
+	 * <br>正确示例:QueryWrapper<JeecgDemo> queryWrapper = new QueryWrapper<JeecgDemo>();
+	 * <br>3.也可以不使用这个方法直接调用 {@link #initQueryWrapper}直接获取实例
+	 */
+	private static void installMplus(QueryWrapper<?> queryWrapper,Object searchObj,Map<String, String[]> parameterMap) {
+		
+		/*
+		 * 注意:权限查询由前端配置数据规则 当一个人有多个所属部门时候 可以在规则配置包含条件 orgCode 包含 #{sys_org_code}
+		但是不支持在自定义SQL中写orgCode in #{sys_org_code} 
+		当一个人只有一个部门 就直接配置等于条件: orgCode 等于 #{sys_org_code} 或者配置自定义SQL: orgCode = '#{sys_org_code}'
+		*/
+		
+		//区间条件组装 模糊查询 高级查询组装 简单排序 权限查询
+		PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(searchObj);
+		Map<String,SysPermissionDataRuleModel> ruleMap = getRuleMap();
+		
+		//权限规则自定义SQL表达式
+		for (String c : ruleMap.keySet()) {
+			if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
+				queryWrapper.and(i ->i.apply(getSqlRuleValue(ruleMap.get(c).getRuleValue())));
+			}
+		}
+		
+		String name, type, column;
+		// update-begin--Author:taoyan  Date:20200923 for:issues/1671 如果字段加注解了@TableField(exist = false),不走DB查询-------
+		//定义实体字段和数据库字段名称的映射 高级查询中 只能获取实体字段 如果设置TableField注解 那么查询条件会出问题
+		Map<String,String> fieldColumnMap = new HashMap<>(5);
+		for (int i = 0; i < origDescriptors.length; i++) {
+			//aliasName = origDescriptors[i].getName();  mybatis  不存在实体属性 不用处理别名的情况
+			name = origDescriptors[i].getName();
+			type = origDescriptors[i].getPropertyType().toString();
+			try {
+				if (judgedIsUselessField(name)|| !PropertyUtils.isReadable(searchObj, name)) {
+					continue;
+				}
+
+				Object value = PropertyUtils.getSimpleProperty(searchObj, name);
+				column = ReflectHelper.getTableFieldName(searchObj.getClass(), name);
+				if(column==null){
+					//column为null只有一种情况 那就是 添加了注解@TableField(exist = false) 后续都不用处理了
+					continue;
+				}
+				fieldColumnMap.put(name,column);
+				//数据权限查询
+				if(ruleMap.containsKey(name)) {
+					addRuleToQueryWrapper(ruleMap.get(name), column, origDescriptors[i].getPropertyType(), queryWrapper);
+				}
+				//区间查询
+				doIntervalQuery(queryWrapper, parameterMap, type, name, column);
+				//判断单值  参数带不同标识字符串 走不同的查询
+				//TODO 这种前后带逗号的支持分割后模糊查询(多选字段查询生效) 示例:,1,3,
+				if (null != value && value.toString().startsWith(COMMA) && value.toString().endsWith(COMMA)) {
+					String multiLikeval = value.toString().replace(",,", COMMA);
+					String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(COMMA);
+					final String field = oConvertUtils.camelToUnderline(column);
+					if(vals.length>1) {
+						queryWrapper.and(j -> {
+                            log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}", field, "like", vals[0]);
+							j = j.like(field,vals[0]);
+							for (int k=1;k<vals.length;k++) {
+								j = j.or().like(field,vals[k]);
+								log.info("---查询过滤器,Query规则 .or()---field:{}, rule:{}, value:{}", field, "like", vals[k]);
+							}
+							//return j;
+						});
+					}else {
+						log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}", field, "like", vals[0]);
+						queryWrapper.and(j -> j.like(field,vals[0]));
+					}
+				}else {
+					//根据参数值带什么关键字符串判断走什么类型的查询
+					QueryRuleEnum rule = convert2Rule(value);
+					value = replaceValue(rule,value);
+					// add -begin 添加判断为字符串时设为全模糊查询
+					//if( (rule==null || QueryRuleEnum.EQ.equals(rule)) && "class java.lang.String".equals(type)) {
+						// 可以设置左右模糊或全模糊,因人而异
+						//rule = QueryRuleEnum.LIKE;
+					//}
+					// add -end 添加判断为字符串时设为全模糊查询
+					addEasyQuery(queryWrapper, column, rule, value);
+				}
+				
+			} catch (Exception e) {
+				log.error(e.getMessage(), e);
+			}
+		}
+		// 排序逻辑 处理
+		doMultiFieldsOrder(queryWrapper, parameterMap, fieldColumnMap);
+				
+		//高级查询
+		doSuperQuery(queryWrapper, parameterMap, fieldColumnMap);
+		// update-end--Author:taoyan  Date:20200923 for:issues/1671 如果字段加注解了@TableField(exist = false),不走DB查询-------
+		
+	}
+
+
+	/**
+	 * 区间查询
+	 * @param queryWrapper query对象
+	 * @param parameterMap 参数map
+	 * @param type         字段类型
+	 * @param filedName    字段名称
+	 * @param columnName   列名称
+	 */
+	private static void doIntervalQuery(QueryWrapper<?> queryWrapper, Map<String, String[]> parameterMap, String type, String filedName, String columnName) throws ParseException {
+		// 添加 判断是否有区间值
+		String endValue = null,beginValue = null;
+		if (parameterMap != null && parameterMap.containsKey(filedName + BEGIN)) {
+			beginValue = parameterMap.get(filedName + BEGIN)[0].trim();
+			addQueryByRule(queryWrapper, columnName, type, beginValue, QueryRuleEnum.GE);
+
+		}
+		if (parameterMap != null && parameterMap.containsKey(filedName + END)) {
+			endValue = parameterMap.get(filedName + END)[0].trim();
+			addQueryByRule(queryWrapper, columnName, type, endValue, QueryRuleEnum.LE);
+		}
+		//多值查询
+		if (parameterMap != null && parameterMap.containsKey(filedName + MULTI)) {
+			endValue = parameterMap.get(filedName + MULTI)[0].trim();
+			addQueryByRule(queryWrapper, columnName.replace(MULTI,""), type, endValue, QueryRuleEnum.IN);
+		}
+	}
+	
+	private static void doMultiFieldsOrder(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap, Map<String,String> fieldColumnMap) {
+		Set<String> allFields = fieldColumnMap.keySet();
+		String column=null,order=null;
+		if(parameterMap!=null&& parameterMap.containsKey(ORDER_COLUMN)) {
+			column = parameterMap.get(ORDER_COLUMN)[0];
+		}
+		if(parameterMap!=null&& parameterMap.containsKey(ORDER_TYPE)) {
+			order = parameterMap.get(ORDER_TYPE)[0];
+		}
+        log.debug("排序规则>>列:" + column + ",排序方式:" + order);
+
+		//update-begin-author:scott date:2022-11-07 for:避免用户自定义表无默认字段{创建时间},导致排序报错
+		//TODO 避免用户自定义表无默认字段创建时间,导致排序报错
+		if(DataBaseConstant.CREATE_TIME.equals(column) && !fieldColumnMap.containsKey(DataBaseConstant.CREATE_TIME)){
+			column = "id";
+			log.warn("检测到实体里没有字段createTime,改成采用ID排序!");
+		}
+		//update-end-author:scott date:2022-11-07 for:避免用户自定义表无默认字段{创建时间},导致排序报错
+		
+		if (oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
+			//字典字段,去掉字典翻译文本后缀
+			if(column.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) {
+				column = column.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX));
+			}
+
+			//update-begin-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时,使用SQL注入生效
+			//判断column是不是当前实体的
+			log.debug("当前字段有:"+ allFields);
+			if (!allColumnExist(column, allFields)) {
+				throw new JeecgBootException("请注意,将要排序的列字段不存在:" + column);
+			}
+			//update-end-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时,使用SQL注入生效
+
+			//update-begin-author:scott date:2022-10-10 for:【jeecg-boot/issues/I5FJU6】doMultiFieldsOrder() 多字段排序方法存在问题
+			//多字段排序方法没有读取 MybatisPlus 注解 @TableField 里 value 的值
+			if (column.contains(",")) {
+				List<String> columnList = Arrays.asList(column.split(","));
+				String columnStrNew = columnList.stream().map(c -> fieldColumnMap.get(c)).collect(Collectors.joining(","));
+				if (oConvertUtils.isNotEmpty(columnStrNew)) {
+					column = columnStrNew;
+				}
+			}else{
+				column = fieldColumnMap.get(column);
+			}
+			//update-end-author:scott date:2022-10-10 for:【jeecg-boot/issues/I5FJU6】doMultiFieldsOrder() 多字段排序方法存在问题
+
+			//SQL注入check
+			SqlInjectionUtil.filterContent(column);
+
+			//update-begin--Author:scott  Date:20210531 for:36 多条件排序无效问题修正-------
+			// 排序规则修改
+			// 将现有排序 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1,column2 desc"
+			// 修改为 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1 desc,column2 desc"
+			if (order.toUpperCase().indexOf(ORDER_TYPE_ASC)>=0) {
+				queryWrapper.orderByAsc(SqlInjectionUtil.getSqlInjectSortFields(column.split(",")));
+			} else {
+				queryWrapper.orderByDesc(SqlInjectionUtil.getSqlInjectSortFields(column.split(",")));
+			}
+			//update-end--Author:scott  Date:20210531 for:36 多条件排序无效问题修正-------
+		}
+	}
+
+	//update-begin-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时,使用SQL注入生效
+	/**
+	 * 多字段排序 判断所传字段是否存在
+	 * @return
+	 */
+	private static boolean allColumnExist(String columnStr, Set<String> allFields){
+		boolean exist = true;
+		if(columnStr.indexOf(COMMA)>=0){
+			String[] arr = columnStr.split(COMMA);
+			for(String column: arr){
+				if(!allFields.contains(column)){
+					exist = false;
+					break;
+				}
+			}
+		}else{
+			exist = allFields.contains(columnStr);
+		}
+		return exist;
+	}
+	//update-end-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时,使用SQL注入生效
+	
+	/**
+	 * 高级查询
+	 * @param queryWrapper 查询对象
+	 * @param parameterMap 参数对象
+	 * @param fieldColumnMap 实体字段和数据库列对应的map
+	 */
+	private static void doSuperQuery(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap, Map<String,String> fieldColumnMap) {
+		if(parameterMap!=null&& parameterMap.containsKey(SUPER_QUERY_PARAMS)){
+			String superQueryParams = parameterMap.get(SUPER_QUERY_PARAMS)[0];
+			String superQueryMatchType = parameterMap.get(SUPER_QUERY_MATCH_TYPE) != null ? parameterMap.get(SUPER_QUERY_MATCH_TYPE)[0] : MatchTypeEnum.AND.getValue();
+            MatchTypeEnum matchType = MatchTypeEnum.getByValue(superQueryMatchType);
+            // update-begin--Author:sunjianlei  Date:20200325 for:高级查询的条件要用括号括起来,防止和用户的其他条件冲突 -------
+            try {
+                superQueryParams = URLDecoder.decode(superQueryParams, "UTF-8");
+                List<QueryCondition> conditions = JSON.parseArray(superQueryParams, QueryCondition.class);
+                if (conditions == null || conditions.size() == 0) {
+                    return;
+                }
+				// update-begin-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
+				List<QueryCondition> filterConditions = conditions.stream().filter(
+						rule -> oConvertUtils.isNotEmpty(rule.getField())
+								&& oConvertUtils.isNotEmpty(rule.getRule())
+								&& oConvertUtils.isNotEmpty(rule.getVal())
+				).collect(Collectors.toList());
+				if (filterConditions.size() == 0) {
+					return;
+				}
+				// update-end-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
+                log.debug("---高级查询参数-->" + filterConditions);
+
+                queryWrapper.and(andWrapper -> {
+                    for (int i = 0; i < filterConditions.size(); i++) {
+                        QueryCondition rule = filterConditions.get(i);
+                        if (oConvertUtils.isNotEmpty(rule.getField())
+                                && oConvertUtils.isNotEmpty(rule.getRule())
+                                && oConvertUtils.isNotEmpty(rule.getVal())) {
+
+                            log.debug("SuperQuery ==> " + rule.toString());
+
+                            //update-begin-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错
+							Object queryValue = rule.getVal();
+                            if("date".equals(rule.getType())){
+								queryValue = DateUtils.str2Date(rule.getVal(),DateUtils.date_sdf.get());
+							}else if("datetime".equals(rule.getType())){
+								queryValue = DateUtils.str2Date(rule.getVal(), DateUtils.datetimeFormat.get());
+							}
+							// update-begin--author:sunjianlei date:20210702 for:【/issues/I3VR8E】高级查询没有类型转换,查询参数都是字符串类型 ----
+							String dbType = rule.getDbType();
+							if (oConvertUtils.isNotEmpty(dbType)) {
+								try {
+									String valueStr = String.valueOf(queryValue);
+									switch (dbType.toLowerCase().trim()) {
+										case "int":
+											queryValue = Integer.parseInt(valueStr);
+											break;
+										case "bigdecimal":
+											queryValue = new BigDecimal(valueStr);
+											break;
+										case "short":
+											queryValue = Short.parseShort(valueStr);
+											break;
+										case "long":
+											queryValue = Long.parseLong(valueStr);
+											break;
+										case "float":
+											queryValue = Float.parseFloat(valueStr);
+											break;
+										case "double":
+											queryValue = Double.parseDouble(valueStr);
+											break;
+										case "boolean":
+											queryValue = Boolean.parseBoolean(valueStr);
+											break;
+                                        default:
+									}
+								} catch (Exception e) {
+									log.error("高级查询值转换失败:", e);
+								}
+							}
+							// update-begin--author:sunjianlei date:20210702 for:【/issues/I3VR8E】高级查询没有类型转换,查询参数都是字符串类型 ----
+                            addEasyQuery(andWrapper, fieldColumnMap.get(rule.getField()), QueryRuleEnum.getByValue(rule.getRule()), queryValue);
+							//update-end-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错
+
+                            // 如果拼接方式是OR,就拼接OR
+                            if (MatchTypeEnum.OR == matchType && i < (filterConditions.size() - 1)) {
+                                andWrapper.or();
+                            }
+                        }
+                    }
+                    //return andWrapper;
+                });
+            } catch (UnsupportedEncodingException e) {
+                log.error("--高级查询参数转码失败:" + superQueryParams, e);
+            } catch (Exception e) {
+                log.error("--高级查询拼接失败:" + e.getMessage());
+                e.printStackTrace();
+            }
+            // update-end--Author:sunjianlei  Date:20200325 for:高级查询的条件要用括号括起来,防止和用户的其他条件冲突 -------
+		}
+		//log.info(" superQuery getCustomSqlSegment: "+ queryWrapper.getCustomSqlSegment());
+	}
+	/**
+	 * 根据所传的值 转化成对应的比较方式
+	 * 支持><= like in !
+	 * @param value
+	 * @return
+	 */
+	public static QueryRuleEnum convert2Rule(Object value) {
+		// 避免空数据
+		// update-begin-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常
+		if (value == null) {
+			return QueryRuleEnum.EQ;
+		}
+		String val = (value + "").toString().trim();
+		if (val.length() == 0) {
+			return QueryRuleEnum.EQ;
+		}
+		// update-end-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常
+		QueryRuleEnum rule =null;
+
+		//update-begin--Author:scott  Date:20190724 for:initQueryWrapper组装sql查询条件错误 #284-------------------
+		//TODO 此处规则,只适用于 le lt ge gt
+		// step 2 .>= =<
+        int length2 = 2;
+        int length3 = 3;
+		if (rule == null && val.length() >= length3) {
+			if(QUERY_SEPARATE_KEYWORD.equals(val.substring(length2, length3))){
+				rule = QueryRuleEnum.getByValue(val.substring(0, 2));
+			}
+		}
+		// step 1 .> <
+		if (rule == null && val.length() >= length2) {
+			if(QUERY_SEPARATE_KEYWORD.equals(val.substring(1, length2))){
+				rule = QueryRuleEnum.getByValue(val.substring(0, 1));
+			}
+		}
+		//update-end--Author:scott  Date:20190724 for:initQueryWrapper组装sql查询条件错误 #284---------------------
+
+		// step 3 like
+		//update-begin-author:taoyan for: /issues/3382 默认带*就走模糊,但是如果只有一个*,那么走等于查询
+		if(rule == null && val.equals(STAR)){
+			rule = QueryRuleEnum.EQ;
+		}
+		//update-end-author:taoyan for: /issues/3382  默认带*就走模糊,但是如果只有一个*,那么走等于查询
+		if (rule == null && val.contains(STAR)) {
+			if (val.startsWith(STAR) && val.endsWith(STAR)) {
+				rule = QueryRuleEnum.LIKE;
+			} else if (val.startsWith(STAR)) {
+				rule = QueryRuleEnum.LEFT_LIKE;
+			} else if(val.endsWith(STAR)){
+				rule = QueryRuleEnum.RIGHT_LIKE;
+			}
+		}
+
+		// step 4 in
+		if (rule == null && val.contains(COMMA)) {
+			//TODO in 查询这里应该有个bug  如果一字段本身就是多选 此时用in查询 未必能查询出来
+			rule = QueryRuleEnum.IN;
+		}
+		// step 5 != 
+		if(rule == null && val.startsWith(NOT_EQUAL)){
+			rule = QueryRuleEnum.NE;
+		}
+		// step 6 xx+xx+xx 这种情况适用于如果想要用逗号作精确查询 但是系统默认逗号走in 所以可以用++替换【此逻辑作废】
+		if(rule == null && val.indexOf(QUERY_COMMA_ESCAPE)>0){
+			rule = QueryRuleEnum.EQ_WITH_ADD;
+		}
+
+		//update-begin--Author:taoyan  Date:20201229 for:initQueryWrapper组装sql查询条件错误 #284---------------------
+		//特殊处理:Oracle的表达式to_date('xxx','yyyy-MM-dd')含有逗号,会被识别为in查询,转为等于查询
+		if(rule == QueryRuleEnum.IN && val.indexOf(YYYY_MM_DD)>=0 && val.indexOf(TO_DATE)>=0){
+			rule = QueryRuleEnum.EQ;
+		}
+		//update-end--Author:taoyan  Date:20201229 for:initQueryWrapper组装sql查询条件错误 #284---------------------
+
+		return rule != null ? rule : QueryRuleEnum.EQ;
+	}
+	
+	/**
+	 * 替换掉关键字字符
+	 * 
+	 * @param rule
+	 * @param value
+	 * @return
+	 */
+	private static Object replaceValue(QueryRuleEnum rule, Object value) {
+		if (rule == null) {
+			return null;
+		}
+		if (! (value instanceof String)){
+			return value;
+		}
+		String val = (value + "").toString().trim();
+		//update-begin-author:taoyan date:20220302 for: 查询条件的值为等号(=)bug #3443
+		if(QueryRuleEnum.EQ.getValue().equals(val)){
+			return val;
+		}
+		//update-end-author:taoyan date:20220302 for: 查询条件的值为等号(=)bug #3443
+		if (rule == QueryRuleEnum.LIKE) {
+			value = val.substring(1, val.length() - 1);
+			//mysql 模糊查询之特殊字符下划线 (_、\)
+			value = specialStrConvert(value.toString());
+		} else if (rule == QueryRuleEnum.LEFT_LIKE || rule == QueryRuleEnum.NE) {
+			value = val.substring(1);
+			//mysql 模糊查询之特殊字符下划线 (_、\)
+			value = specialStrConvert(value.toString());
+		} else if (rule == QueryRuleEnum.RIGHT_LIKE) {
+			value = val.substring(0, val.length() - 1);
+			//mysql 模糊查询之特殊字符下划线 (_、\)
+			value = specialStrConvert(value.toString());
+		} else if (rule == QueryRuleEnum.IN) {
+			value = val.split(",");
+		} else if (rule == QueryRuleEnum.EQ_WITH_ADD) {
+			value = val.replaceAll("\\+\\+", COMMA);
+		}else {
+			//update-begin--Author:scott  Date:20190724 for:initQueryWrapper组装sql查询条件错误 #284-------------------
+			if(val.startsWith(rule.getValue())){
+				//TODO 此处逻辑应该注释掉-> 如果查询内容中带有查询匹配规则符号,就会被截取的(比如:>=您好)
+				value = val.replaceFirst(rule.getValue(),"");
+			}else if(val.startsWith(rule.getCondition()+QUERY_SEPARATE_KEYWORD)){
+				value = val.replaceFirst(rule.getCondition()+QUERY_SEPARATE_KEYWORD,"").trim();
+			}
+			//update-end--Author:scott  Date:20190724 for:initQueryWrapper组装sql查询条件错误 #284-------------------
+		}
+		return value;
+	}
+	
+	private static void addQueryByRule(QueryWrapper<?> queryWrapper,String name,String type,String value,QueryRuleEnum rule) throws ParseException {
+		if(oConvertUtils.isNotEmpty(value)) {
+			//update-begin--Author:sunjianlei  Date:20220104 for:【JTC-409】修复逗号分割情况下没有转换类型,导致类型严格的数据库查询报错 -------------------
+			// 针对数字类型字段,多值查询
+			if(value.contains(COMMA)){
+				Object[] temp = Arrays.stream(value.split(COMMA)).map(v -> {
+					try {
+						return QueryGenerator.parseByType(v, type, rule);
+					} catch (ParseException e) {
+						e.printStackTrace();
+						return v;
+					}
+				}).toArray();
+				addEasyQuery(queryWrapper, name, rule, temp);
+				return;
+			}
+			Object temp = QueryGenerator.parseByType(value, type, rule);
+			addEasyQuery(queryWrapper, name, rule, temp);
+			//update-end--Author:sunjianlei  Date:20220104 for:【JTC-409】修复逗号分割情况下没有转换类型,导致类型严格的数据库查询报错 -------------------
+		}
+	}
+
+	/**
+	 * 根据类型转换给定的值
+	 * @param value
+	 * @param type
+	 * @param rule
+	 * @return
+	 * @throws ParseException
+	 */
+	private static Object parseByType(String value, String type, QueryRuleEnum rule) throws ParseException {
+		Object temp;
+		switch (type) {
+			case "class java.lang.Integer":
+				temp =  Integer.parseInt(value);
+				break;
+			case "class java.math.BigDecimal":
+				temp =  new BigDecimal(value);
+				break;
+			case "class java.lang.Short":
+				temp =  Short.parseShort(value);
+				break;
+			case "class java.lang.Long":
+				temp =  Long.parseLong(value);
+				break;
+			case "class java.lang.Float":
+				temp =   Float.parseFloat(value);
+				break;
+			case "class java.lang.Double":
+				temp =  Double.parseDouble(value);
+				break;
+			case "class java.util.Date":
+				temp = getDateQueryByRule(value, rule);
+				break;
+			default:
+				temp = value;
+				break;
+		}
+		return temp;
+	}
+	
+	/**
+	 * 获取日期类型的值
+	 * @param value
+	 * @param rule
+	 * @return
+	 * @throws ParseException
+	 */
+	private static Date getDateQueryByRule(String value,QueryRuleEnum rule) throws ParseException {
+		Date date = null;
+		int length = 10;
+		if(value.length()==length) {
+			if(rule==QueryRuleEnum.GE) {
+				//比较大于
+				date = getTime().parse(value + " 00:00:00");
+			}else if(rule==QueryRuleEnum.LE) {
+				//比较小于
+				date = getTime().parse(value + " 23:59:59");
+			}
+			//TODO 日期类型比较特殊 可能oracle下不一定好使
+		}
+		if(date==null) {
+			date = getTime().parse(value);
+		}
+		return date;
+	}
+	
+	/**
+	  * 根据规则走不同的查询
+	 * @param queryWrapper QueryWrapper
+	 * @param name         字段名字
+	 * @param rule         查询规则
+	 * @param value        查询条件值
+	 */
+	public static void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
+		if (name==null || value == null || rule == null || oConvertUtils.isEmpty(value)) {
+			return;
+		}
+		name = oConvertUtils.camelToUnderline(name);
+		log.debug("---高级查询 Query规则---field:{} , rule:{} , value:{}",name,rule.getValue(),value);
+		switch (rule) {
+		case GT:
+			queryWrapper.gt(name, value);
+			break;
+		case GE:
+			queryWrapper.ge(name, value);
+			break;
+		case LT:
+			queryWrapper.lt(name, value);
+			break;
+		case LE:
+			queryWrapper.le(name, value);
+			break;
+		case EQ:
+		case EQ_WITH_ADD:
+			queryWrapper.eq(name, value);
+			break;
+		case NE:
+			queryWrapper.ne(name, value);
+			break;
+		case IN:
+			if(value instanceof String) {
+				queryWrapper.in(name, (Object[])value.toString().split(COMMA));
+			}else if(value instanceof String[]) {
+				queryWrapper.in(name, (Object[]) value);
+			}
+			//update-begin-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
+			else if(value.getClass().isArray()) {
+				queryWrapper.in(name, (Object[])value);
+			}else {
+				queryWrapper.in(name, value);
+			}
+			//update-end-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
+			break;
+		case LIKE:
+			queryWrapper.like(name, value);
+			break;
+		case LEFT_LIKE:
+			queryWrapper.likeLeft(name, value);
+			break;
+		case RIGHT_LIKE:
+			queryWrapper.likeRight(name, value);
+			break;
+		default:
+			log.info("--查询规则未匹配到---");
+			break;
+		}
+	}
+	/**
+	 * 
+	 * @param name
+	 * @return
+	 */
+	private static boolean judgedIsUselessField(String name) {
+		return "class".equals(name) || "ids".equals(name)
+				|| "page".equals(name) || "rows".equals(name)
+				|| "sort".equals(name) || "order".equals(name);
+	}
+
+	
+
+	/**
+	 * 获取请求对应的数据权限规则 TODO 相同列权限多个 有问题
+	 * @return
+	 */
+	public static Map<String, SysPermissionDataRuleModel> getRuleMap() {
+		Map<String, SysPermissionDataRuleModel> ruleMap = new HashMap<>(5);
+		List<SysPermissionDataRuleModel> list = null;
+		//update-begin-author:taoyan date:2023-6-1 for:QQYUN-5441 【简流】获取多个用户/部门/角色 设置部门查询 报错
+		try {
+			list = JeecgDataAutorUtils.loadDataSearchConditon();
+		}catch (Exception e){
+			log.error("根据request对象获取权限数据失败,可能是定时任务中执行的。", e);
+		}
+		//update-end-author:taoyan date:2023-6-1 for:QQYUN-5441 【简流】获取多个用户/部门/角色 设置部门查询 报错
+		if(list != null&&list.size()>0){
+			if(list.get(0)==null){
+				return ruleMap;
+			}
+			for (SysPermissionDataRuleModel rule : list) {
+				String column = rule.getRuleColumn();
+				if(QueryRuleEnum.SQL_RULES.getValue().equals(rule.getRuleConditions())) {
+					column = SQL_RULES_COLUMN+rule.getId();
+				}
+				ruleMap.put(column, rule);
+			}
+		}
+		return ruleMap;
+	}
+	
+	private static void addRuleToQueryWrapper(SysPermissionDataRuleModel dataRule, String name, Class propertyType, QueryWrapper<?> queryWrapper) {
+		QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
+		if(rule.equals(QueryRuleEnum.IN) && ! propertyType.equals(String.class)) {
+			String[] values = dataRule.getRuleValue().split(",");
+			Object[] objs = new Object[values.length];
+			for (int i = 0; i < values.length; i++) {
+				objs[i] = NumberUtils.parseNumber(values[i], propertyType);
+			}
+			addEasyQuery(queryWrapper, name, rule, objs);
+		}else {
+			if (propertyType.equals(String.class)) {
+				addEasyQuery(queryWrapper, name, rule, converRuleValue(dataRule.getRuleValue()));
+			}else if (propertyType.equals(Date.class)) {
+				String dateStr =converRuleValue(dataRule.getRuleValue());
+                int length = 10;
+				if(dateStr.length()==length){
+					addEasyQuery(queryWrapper, name, rule, DateUtils.str2Date(dateStr,DateUtils.date_sdf.get()));
+				}else{
+					addEasyQuery(queryWrapper, name, rule, DateUtils.str2Date(dateStr,DateUtils.datetimeFormat.get()));
+				}
+			}else {
+				addEasyQuery(queryWrapper, name, rule, NumberUtils.parseNumber(dataRule.getRuleValue(), propertyType));
+			}
+		}
+	}
+	
+	public static String converRuleValue(String ruleValue) {
+		String value = JwtUtil.getUserSystemData(ruleValue,null);
+		return value!= null ? value : ruleValue;
+	}
+
+	/**
+	* @author: scott
+	* @Description: 去掉值前后单引号
+	* @date: 2020/3/19 21:26
+	* @param ruleValue: 
+	* @Return: java.lang.String
+	*/
+	public static String trimSingleQuote(String ruleValue) {
+		if (oConvertUtils.isEmpty(ruleValue)) {
+			return "";
+		}
+		if (ruleValue.startsWith(QueryGenerator.SQL_SQ)) {
+			ruleValue = ruleValue.substring(1);
+		}
+		if (ruleValue.endsWith(QueryGenerator.SQL_SQ)) {
+			ruleValue = ruleValue.substring(0, ruleValue.length() - 1);
+		}
+		return ruleValue;
+	}
+	
+	public static String getSqlRuleValue(String sqlRule){
+		try {
+			Set<String> varParams = getSqlRuleParams(sqlRule);
+			for(String var:varParams){
+				String tempValue = converRuleValue(var);
+				sqlRule = sqlRule.replace("#{"+var+"}",tempValue);
+			}
+		} catch (Exception e) {
+			log.error(e.getMessage(), e);
+		}
+		return sqlRule;
+	}
+	
+	/**
+	 * 获取sql中的#{key} 这个key组成的set
+	 */
+	public static Set<String> getSqlRuleParams(String sql) {
+		if(oConvertUtils.isEmpty(sql)){
+			return null;
+		}
+		Set<String> varParams = new HashSet<String>();
+		String regex = "\\#\\{\\w+\\}";
+		
+		Pattern p = Pattern.compile(regex);
+		Matcher m = p.matcher(sql);
+		while(m.find()){
+			String var = m.group();
+			varParams.add(var.substring(var.indexOf("{")+1,var.indexOf("}")));
+		}
+		return varParams;
+	}
+	
+	/**
+	 * 获取查询条件 
+	 * @param field
+	 * @param alias
+	 * @param value
+	 * @param isString
+	 * @return
+	 */
+	public static String getSingleQueryConditionSql(String field,String alias,Object value,boolean isString) {
+		return SqlConcatUtil.getSingleQueryConditionSql(field, alias, value, isString,null);
+	}
+	
+	/**
+	 *   根据权限相关配置生成相关的SQL 语句
+	 * @param clazz
+	 * @return
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public static String installAuthJdbc(Class<?> clazz) {
+		StringBuffer sb = new StringBuffer();
+		//权限查询
+		Map<String,SysPermissionDataRuleModel> ruleMap = getRuleMap();
+		PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(clazz);
+		String sqlAnd = " and ";
+		for (String c : ruleMap.keySet()) {
+			if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
+				sb.append(sqlAnd+getSqlRuleValue(ruleMap.get(c).getRuleValue()));
+			}
+		}
+		String name, column;
+		for (int i = 0; i < origDescriptors.length; i++) {
+			name = origDescriptors[i].getName();
+			if (judgedIsUselessField(name)) {
+				continue;
+			}
+			if(ruleMap.containsKey(name)) {
+				column = ReflectHelper.getTableFieldName(clazz, name);
+				if(column==null){
+					continue;
+				}
+				SysPermissionDataRuleModel dataRule = ruleMap.get(name);
+				QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
+				Class propType = origDescriptors[i].getPropertyType();
+				boolean isString = propType.equals(String.class);
+				Object value;
+				if(isString) {
+					value = converRuleValue(dataRule.getRuleValue());
+				}else {
+					value = NumberUtils.parseNumber(dataRule.getRuleValue(),propType);
+				}
+				String filedSql = SqlConcatUtil.getSingleSqlByRule(rule, oConvertUtils.camelToUnderline(column), value,isString);
+				sb.append(sqlAnd+filedSql);
+			}
+		}
+		log.info("query auth sql is:"+sb.toString());
+		return sb.toString();
+	}
+	
+	/**
+	  * 根据权限相关配置 组装mp需要的权限
+	 * @param queryWrapper
+	 * @param clazz
+	 * @return
+	 */
+	public static void installAuthMplus(QueryWrapper<?> queryWrapper,Class<?> clazz) {
+		//权限查询
+		Map<String,SysPermissionDataRuleModel> ruleMap = getRuleMap();
+		PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(clazz);
+		for (String c : ruleMap.keySet()) {
+			if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
+				queryWrapper.and(i ->i.apply(getSqlRuleValue(ruleMap.get(c).getRuleValue())));
+			}
+		}
+		String name, column;
+		for (int i = 0; i < origDescriptors.length; i++) {
+			name = origDescriptors[i].getName();
+			if (judgedIsUselessField(name)) {
+				continue;
+			}
+			column = ReflectHelper.getTableFieldName(clazz, name);
+			if(column==null){
+				continue;
+			}
+			if(ruleMap.containsKey(name)) {
+				addRuleToQueryWrapper(ruleMap.get(name), column, origDescriptors[i].getPropertyType(), queryWrapper);
+			}
+		}
+	}
+
+	/**
+	 * 转换sql中的系统变量
+	 * @param sql
+	 * @return
+	 */
+	public static String convertSystemVariables(String sql){
+		return getSqlRuleValue(sql);
+	}
+
+	/**
+	 * 获取系统数据库类型
+	 */
+	private static String getDbType(){
+		return CommonUtils.getDatabaseType();
+	}
+
+	/**
+	 * mysql 模糊查询之特殊字符下划线 (_、\)
+	 *
+	 * @param value:
+	 * @Return: java.lang.String
+	 */
+	private static String specialStrConvert(String value) {
+		if (DataBaseConstant.DB_TYPE_MYSQL.equals(getDbType()) || DataBaseConstant.DB_TYPE_MARIADB.equals(getDbType())) {
+			String[] specialStr = QueryGenerator.LIKE_MYSQL_SPECIAL_STRS.split(",");
+			for (String str : specialStr) {
+				if (value.indexOf(str) !=-1) {
+					value = value.replace(str, "\\" + str);
+				}
+			}
+		}
+		return value;
+	}
+}

+ 102 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryRuleEnum.java

@@ -0,0 +1,102 @@
+package org.jeecg.common.system.query;
+
+import org.jeecg.common.util.oConvertUtils;
+
+/**
+ * Query 规则 常量
+ * @Author Scott
+ * @Date 2019年02月14日
+ */
+public enum QueryRuleEnum {
+
+    /**查询规则 大于*/
+    GT(">","gt","大于"),
+    /**查询规则 大于等于*/
+    GE(">=","ge","大于等于"),
+    /**查询规则 小于*/
+    LT("<","lt","小于"),
+    /**查询规则 小于等于*/
+    LE("<=","le","小于等于"),
+    /**查询规则 等于*/
+    EQ("=","eq","等于"),
+    /**查询规则 不等于*/
+    NE("!=","ne","不等于"),
+    /**查询规则 包含*/
+    IN("IN","in","包含"),
+    /**查询规则 全模糊*/
+    LIKE("LIKE","like","全模糊"),
+    /**查询规则 不模糊包含*/
+    NOT_LIKE("NOT_LIKE","not_like","不模糊包含"),
+    /**查询规则 左模糊*/
+    LEFT_LIKE("LEFT_LIKE","left_like","左模糊"),
+    /**查询规则 右模糊*/
+    RIGHT_LIKE("RIGHT_LIKE","right_like","右模糊"),
+    /**查询规则 带加号等于*/
+    EQ_WITH_ADD("EQWITHADD","eq_with_add","带加号等于"),
+    /**查询规则 多词模糊匹配*/
+    LIKE_WITH_AND("LIKEWITHAND","like_with_and","多词模糊匹配————暂时未用上"),
+    /**查询规则 自定义SQL片段*/
+    SQL_RULES("USE_SQL_RULES","ext","自定义SQL片段"),
+    
+    // ------- 当前表单设计器内专用 -------
+    /** 值为空 */
+    EMPTY("EMPTY","empty","值为空"),
+    /** 值不为空 */
+    NOT_EMPTY("NOT_EMPTY","not_empty","值不为空"),
+    /**查询规则 不包含*/
+    NOT_IN("NOT_IN","not_in","不包含"),
+    /**查询规则 多词匹配*/
+    ELE_MATCH("ELE_MATCH","elemMatch","多词匹配"),
+    /**查询规则 范围查询*/
+    RANGE("RANGE","range","范围查询"),
+    NOT_RANGE("NOT_RANGE","not_range","不在范围查询");
+    // ------- 当前表单设计器内专用 -------
+
+    private String value;
+    
+    private String condition; 
+
+    private String msg;
+
+    QueryRuleEnum(String value, String condition, String msg){
+        this.value = value;
+        this.condition = condition;
+        this.msg = msg;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public String getCondition() {
+		return condition;
+	}
+
+	public void setCondition(String condition) {
+		this.condition = condition;
+	}
+
+	public static QueryRuleEnum getByValue(String value){
+    	if(oConvertUtils.isEmpty(value)) {
+    		return null;
+    	}
+        for(QueryRuleEnum val :values()){
+            if (val.getValue().equals(value) || val.getCondition().equalsIgnoreCase(value)){
+                return val;
+            }
+        }
+        return  null;
+    }
+}

+ 106 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JeecgDataAutorUtils.java

@@ -0,0 +1,106 @@
+package org.jeecg.common.system.util;
+
+import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
+import org.jeecg.common.system.vo.SysUserCacheInfo;
+import org.jeecg.common.util.SpringContextUtils;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @ClassName: JeecgDataAutorUtils
+ * @Description: 数据权限查询规则容器工具类
+ * @Author: 张代浩
+ * @Date: 2012-12-15 下午11:27:39
+ * 
+ */
+public class JeecgDataAutorUtils {
+	
+	public static final String MENU_DATA_AUTHOR_RULES = "MENU_DATA_AUTHOR_RULES";
+	
+	public static final String MENU_DATA_AUTHOR_RULE_SQL = "MENU_DATA_AUTHOR_RULE_SQL";
+	
+	public static final String SYS_USER_INFO = "SYS_USER_INFO";
+
+	/**
+	 * 往链接请求里面,传入数据查询条件
+	 * 
+	 * @param request
+	 * @param dataRules
+	 */
+	public static synchronized void installDataSearchConditon(HttpServletRequest request, List<SysPermissionDataRuleModel> dataRules) {
+		@SuppressWarnings("unchecked")
+        // 1.先从request获取MENU_DATA_AUTHOR_RULES,如果存则获取到LIST
+		List<SysPermissionDataRuleModel> list = (List<SysPermissionDataRuleModel>)loadDataSearchConditon();
+		if (list==null) {
+			// 2.如果不存在,则new一个list
+			list = new ArrayList<SysPermissionDataRuleModel>();
+		}
+		for (SysPermissionDataRuleModel tsDataRule : dataRules) {
+			list.add(tsDataRule);
+		}
+        // 3.往list里面增量存指
+		request.setAttribute(MENU_DATA_AUTHOR_RULES, list);
+	}
+
+	/**
+	 * 获取请求对应的数据权限规则
+	 * 
+	 * @return
+	 */
+	@SuppressWarnings("unchecked")
+	public static synchronized List<SysPermissionDataRuleModel> loadDataSearchConditon() {
+		return (List<SysPermissionDataRuleModel>) SpringContextUtils.getHttpServletRequest().getAttribute(MENU_DATA_AUTHOR_RULES);
+				
+	}
+
+	/**
+	 * 获取请求对应的数据权限SQL
+	 * 
+	 * @return
+	 */
+	public static synchronized String loadDataSearchConditonSqlString() {
+		return (String) SpringContextUtils.getHttpServletRequest().getAttribute(MENU_DATA_AUTHOR_RULE_SQL);
+	}
+
+	/**
+	 * 往链接请求里面,传入数据查询条件
+	 * 
+	 * @param request
+	 * @param sql
+	 */
+	public static synchronized void installDataSearchConditon(HttpServletRequest request, String sql) {
+		String ruleSql = (String) loadDataSearchConditonSqlString();
+		if (!StringUtils.hasText(ruleSql)) {
+			request.setAttribute(MENU_DATA_AUTHOR_RULE_SQL,sql);
+		}
+	}
+
+	/**
+	 * 将用户信息存到request
+	 * @param request
+	 * @param userinfo
+	 */
+	public static synchronized void installUserInfo(HttpServletRequest request, SysUserCacheInfo userinfo) {
+		request.setAttribute(SYS_USER_INFO, userinfo);
+	}
+
+	/**
+	 * 将用户信息存到request
+	 * @param userinfo
+	 */
+	public static synchronized void installUserInfo(SysUserCacheInfo userinfo) {
+		SpringContextUtils.getHttpServletRequest().setAttribute(SYS_USER_INFO, userinfo);
+	}
+
+	/**
+	 * 从request获取用户信息
+	 * @return
+	 */
+	public static synchronized SysUserCacheInfo loadUserInfo() {
+		return (SysUserCacheInfo) SpringContextUtils.getHttpServletRequest().getAttribute(SYS_USER_INFO);
+				
+	}
+}

+ 257 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java

@@ -0,0 +1,257 @@
+package org.jeecg.common.system.util;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Joiner;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.constant.DataBaseConstant;
+import org.jeecg.common.constant.SymbolConstant;
+import org.jeecg.common.exception.JeecgBootException;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.system.vo.SysUserCacheInfo;
+import org.jeecg.common.util.DateUtils;
+import org.jeecg.common.util.SpringContextUtils;
+import org.jeecg.common.util.oConvertUtils;
+
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Date;
+
+/**
+ * @Author Scott
+ * @Date 2018-07-12 14:23
+ * @Desc JWT工具类
+ **/
+@Slf4j
+public class JwtUtil {
+
+	/**Token有效期为7天(Token在reids中缓存时间为两倍)*/
+	public static final long EXPIRE_TIME = 8 * 60 * 60 * 1000;
+	static final String WELL_NUMBER = SymbolConstant.WELL_NUMBER + SymbolConstant.LEFT_CURLY_BRACKET;
+
+    /**
+     *
+     * @param response
+     * @param code
+     * @param errorMsg
+     */
+    public static void responseError(ServletResponse response, Integer code, String errorMsg) {
+		HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+		// issues/I4YH95浏览器显示乱码问题
+		httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
+        Result jsonResult = new Result(code, errorMsg);
+		jsonResult.setSuccess(false);
+        OutputStream os = null;
+        try {
+            os = httpServletResponse.getOutputStream();
+			httpServletResponse.setCharacterEncoding("UTF-8");
+			httpServletResponse.setStatus(code);
+            os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8"));
+            os.flush();
+            os.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+	/**
+	 * 校验token是否正确
+	 *
+	 * @param token  密钥
+	 * @param secret 用户的密码
+	 * @return 是否正确
+	 */
+	public static boolean verify(String token, String username, String secret) {
+		try {
+			// 根据密码生成JWT效验器
+			Algorithm algorithm = Algorithm.HMAC256(secret);
+			JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
+			// 效验TOKEN
+			DecodedJWT jwt = verifier.verify(token);
+			return true;
+		} catch (Exception exception) {
+			return false;
+		}
+	}
+
+	/**
+	 * 获得token中的信息无需secret解密也能获得
+	 *
+	 * @return token中包含的用户名
+	 */
+	public static String getUsername(String token) {
+		try {
+			DecodedJWT jwt = JWT.decode(token);
+			return jwt.getClaim("username").asString();
+		} catch (JWTDecodeException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * 生成签名,5min后过期
+	 *
+	 * @param username 用户名
+	 * @param secret   用户的密码
+	 * @return 加密的token
+	 */
+	public static String sign(String username, String secret) {
+		Algorithm algorithm = Algorithm.HMAC256(secret);
+		// 附带username信息
+		return JWT.create().withClaim("username", username).sign(algorithm);
+	}
+
+	/**
+	 * 根据request中的token获取用户账号
+	 * 
+	 * @param request
+	 * @return
+	 * @throws JeecgBootException
+	 */
+	public static String getUserNameByToken(HttpServletRequest request) throws JeecgBootException {
+		String accessToken = request.getHeader("X-Access-Token");
+		String username = getUsername(accessToken);
+		if (oConvertUtils.isEmpty(username)) {
+			throw new JeecgBootException("未获取到用户");
+		}
+		return username;
+	}
+	
+	/**
+	  *  从session中获取变量
+	 * @param key
+	 * @return
+	 */
+	public static String getSessionData(String key) {
+		//${myVar}%
+		//得到${} 后面的值
+		String moshi = "";
+		String wellNumber = WELL_NUMBER;
+
+		if(key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET)!=-1){
+			 moshi = key.substring(key.indexOf("}")+1);
+		}
+		String returnValue = null;
+		if (key.contains(wellNumber)) {
+			key = key.substring(2,key.indexOf("}"));
+		}
+		if (oConvertUtils.isNotEmpty(key)) {
+			HttpSession session = SpringContextUtils.getHttpServletRequest().getSession();
+			returnValue = (String) session.getAttribute(key);
+		}
+		//结果加上${} 后面的值
+		if(returnValue!=null){returnValue = returnValue + moshi;}
+		return returnValue;
+	}
+	
+	/**
+	  * 从当前用户中获取变量
+	 * @param key
+	 * @param user
+	 * @return
+	 */
+	public static String getUserSystemData(String key, SysUserCacheInfo user) {
+		//1.优先获取 SysUserCacheInfo
+		if(user==null) {
+			try {
+				user = JeecgDataAutorUtils.loadUserInfo();
+			} catch (Exception e) {
+				log.warn("获取用户信息异常:" + e.getMessage());
+			}
+		}
+		//2.通过shiro获取登录用户信息
+		LoginUser sysUser = null;
+		try {
+			sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+		} catch (Exception e) {
+			log.warn("SecurityUtils.getSubject() 获取用户信息异常:" + e.getMessage());
+		}
+
+		//#{sys_user_code}%
+		String moshi = "";
+        String wellNumber = WELL_NUMBER;
+		if(key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET)!=-1){
+			 moshi = key.substring(key.indexOf("}")+1);
+		}
+		String returnValue = null;
+		//针对特殊标示处理#{sysOrgCode},判断替换
+		if (key.contains(wellNumber)) {
+			key = key.substring(2,key.indexOf("}"));
+		} else {
+			key = key;
+		}
+		//替换为当前系统时间(年月日)
+		if (key.equals(DataBaseConstant.SYS_DATE)|| key.toLowerCase().equals(DataBaseConstant.SYS_DATE_TABLE)) {
+			returnValue = DateUtils.formatDate();
+		}
+		//替换为当前系统时间(年月日时分秒)
+		else if (key.equals(DataBaseConstant.SYS_TIME)|| key.toLowerCase().equals(DataBaseConstant.SYS_TIME_TABLE)) {
+			returnValue = DateUtils.now();
+		}
+		//流程状态默认值(默认未发起)
+		else if (key.equals(DataBaseConstant.BPM_STATUS)|| key.toLowerCase().equals(DataBaseConstant.BPM_STATUS_TABLE)) {
+			returnValue = "1";
+		}
+
+		//后台任务获取用户信息异常,导致程序中断
+		if(sysUser==null && user==null){
+			return null;
+		}
+		
+		//替换为系统登录用户帐号
+		if (key.equals(DataBaseConstant.SYS_USER_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_USER_CODE_TABLE)) {
+			if(user==null) {
+				returnValue = sysUser.getUsername();
+			}else {
+				returnValue = user.getSysUserCode();
+			}
+		}
+		//替换为系统登录用户真实名字
+		else if (key.equals(DataBaseConstant.SYS_USER_NAME)|| key.toLowerCase().equals(DataBaseConstant.SYS_USER_NAME_TABLE)) {
+			if(user==null) {
+				returnValue = sysUser.getRealname();
+			}else {
+				returnValue = user.getSysUserName();
+			}
+		}
+		
+		//替换为系统用户登录所使用的机构编码
+		else if (key.equals(DataBaseConstant.SYS_ORG_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_ORG_CODE_TABLE)) {
+			if(user==null) {
+				returnValue = sysUser.getOrgCode();
+			}else {
+				returnValue = user.getSysOrgCode();
+			}
+		}
+		//替换为系统用户所拥有的所有机构编码
+		else if (key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_MULTI_ORG_CODE_TABLE)) {
+			if(user==null){
+				//TODO 暂时使用用户登录部门,存在逻辑缺陷,不是用户所拥有的部门
+				returnValue = sysUser.getOrgCode();
+			}else{
+				if(user.isOneDepart()) {
+					returnValue = user.getSysMultiOrgCode().get(0);
+				}else {
+					returnValue = Joiner.on(",").join(user.getSysMultiOrgCode());
+				}
+			}
+		}
+		if(returnValue!=null){returnValue = returnValue + moshi;}
+		return returnValue;
+	}
+	
+//	public static void main(String[] args) {
+//		 String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjUzMzY1MTMsInVzZXJuYW1lIjoiYWRtaW4ifQ.xjhud_tWCNYBOg_aRlMgOdlZoWFFKB_givNElHNw3X0";
+//		 System.out.println(JwtUtil.getUsername(token));
+//	}
+}

+ 117 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/ResourceUtil.java

@@ -0,0 +1,117 @@
+package org.jeecg.common.system.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.system.annotation.EnumDict;
+import org.jeecg.common.system.vo.DictModel;
+import org.jeecg.common.util.oConvertUtils;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.util.ClassUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 资源加载工具类
+ * @Author taoYan
+ * @Date 2022/7/8 10:40
+ **/
+@Slf4j
+public class ResourceUtil {
+
+
+    /**
+     * 枚举字典数据
+     */
+    private final static Map<String, List<DictModel>> enumDictData = new HashMap<>(5);
+
+    /**
+     * 所有java类
+     */
+    private final static String CLASS_PATTERN="/**/*.class";
+
+    /**
+     * 所有枚举java类
+     */
+
+    private final static String CLASS_ENUM_PATTERN="/**/*Enum.class";
+
+    /**
+     * 包路径 org.jeecg
+     */
+    private final static String BASE_PACKAGE = "org.jeecg";
+
+    /**
+     * 枚举类中获取字典数据的方法名
+     */
+    private final static String METHOD_NAME = "getDictList";
+
+    /**
+     * 获取枚举类对应的字典数据 SysDictServiceImpl#queryAllDictItems()
+     * @return
+     */
+    public static Map<String, List<DictModel>> getEnumDictData(){
+        if(enumDictData.keySet().size()>0){
+            return enumDictData;
+        }
+        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
+        String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + CLASS_ENUM_PATTERN;
+        try {
+            Resource[] resources = resourcePatternResolver.getResources(pattern);
+            MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
+            for (Resource resource : resources) {
+                MetadataReader reader = readerFactory.getMetadataReader(resource);
+                String classname = reader.getClassMetadata().getClassName();
+                Class<?> clazz = Class.forName(classname);
+                EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
+                if (enumDict != null) {
+                    EnumDict annotation = clazz.getAnnotation(EnumDict.class);
+                    String key = annotation.value();
+                    if(oConvertUtils.isNotEmpty(key)){
+                        List<DictModel> list = (List<DictModel>) clazz.getDeclaredMethod(METHOD_NAME).invoke(null);
+                        enumDictData.put(key, list);
+                    }
+                }
+            }
+        }catch (Exception e){
+            log.error("获取枚举类字典数据异常", e.getMessage());
+            // e.printStackTrace();
+        }
+        return enumDictData;
+    }
+
+    /**
+     * 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List)
+     * @param dictCodeList
+     * @param keys
+     * @return
+     */
+    public static Map<String, List<DictModel>> queryManyDictByKeys(List<String> dictCodeList, List<String> keys){
+        if(enumDictData.keySet().size()==0){
+            getEnumDictData();
+        }
+        Map<String, List<DictModel>> map = new HashMap<>();
+        for (String code : enumDictData.keySet()) {
+            if(dictCodeList.indexOf(code)>=0){
+                List<DictModel> dictItemList = enumDictData.get(code);
+                for(DictModel dm: dictItemList){
+                    String value = dm.getValue();
+                    if(keys.indexOf(value)>=0){
+                        List<DictModel> list = new ArrayList<>();
+                        list.add(new DictModel(value, dm.getText()));
+                        map.put(code,list);
+                        break;
+                    }
+                }
+            }
+        }
+        return map;
+    }
+
+}

+ 244 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/SqlConcatUtil.java

@@ -0,0 +1,244 @@
+package org.jeecg.common.system.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.constant.DataBaseConstant;
+import org.jeecg.common.constant.SymbolConstant;
+import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.system.query.QueryRuleEnum;
+import org.jeecg.common.util.CommonUtils;
+import org.jeecg.common.util.oConvertUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Description: 查询过滤器,SQL拼接写法拆成独立工具类
+ * @author:qinfeng
+ * @date 20230904
+ */
+@Slf4j
+public class SqlConcatUtil {
+
+    /**
+     * 获取单个查询条件的值
+     * @param rule
+     * @param field
+     * @param value
+     * @param isString
+     * @return
+     */
+    public static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString) {
+        return getSingleSqlByRule(rule, field, value, isString, null);
+    }
+    
+    /**
+     * 报表获取查询条件 支持多数据源
+     * @param field
+     * @param alias
+     * @param value
+     * @param isString
+     * @param dataBaseType
+     * @return
+     */
+    public static String getSingleQueryConditionSql(String field,String alias,Object value,boolean isString, String dataBaseType) {
+        if (value == null) {
+            return "";
+        }
+        field =  alias+oConvertUtils.camelToUnderline(field);
+        QueryRuleEnum rule = QueryGenerator.convert2Rule(value);
+        return getSingleSqlByRule(rule, field, value, isString, dataBaseType);
+    }
+
+    /**
+     * 获取单个查询条件的值
+     * @param rule
+     * @param field
+     * @param value
+     * @param isString
+     * @param dataBaseType
+     * @return
+     */
+    private static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString, String dataBaseType) {
+        String res = "";
+        switch (rule) {
+            case GT:
+                res =field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
+                break;
+            case GE:
+                res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
+                break;
+            case LT:
+                res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
+                break;
+            case LE:
+                res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
+                break;
+            case EQ:
+                res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
+                break;
+            case EQ_WITH_ADD:
+                res = field+" = "+getFieldConditionValue(value, isString, dataBaseType);
+                break;
+            case NE:
+                res = field+" <> "+getFieldConditionValue(value, isString, dataBaseType);
+                break;
+            case IN:
+                res = field + " in "+getInConditionValue(value, isString);
+                break;
+            case LIKE:
+                res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LIKE);
+                break;
+            case LEFT_LIKE:
+                res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LEFT_LIKE);
+                break;
+            case RIGHT_LIKE:
+                res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.RIGHT_LIKE);
+                break;
+            default:
+                res = field+" = "+getFieldConditionValue(value, isString, dataBaseType);
+                break;
+        }
+        return res;
+    }
+
+    /**
+     * 获取查询条件的值
+     * @param value
+     * @param isString
+     * @param dataBaseType
+     * @return
+     */
+    private static String getFieldConditionValue(Object value,boolean isString, String dataBaseType) {
+        String str = value.toString().trim();
+        if(str.startsWith(SymbolConstant.EXCLAMATORY_MARK)) {
+            str = str.substring(1);
+        }else if(str.startsWith(QueryRuleEnum.GE.getValue())) {
+            str = str.substring(2);
+        }else if(str.startsWith(QueryRuleEnum.LE.getValue())) {
+            str = str.substring(2);
+        }else if(str.startsWith(QueryRuleEnum.GT.getValue())) {
+            str = str.substring(1);
+        }else if(str.startsWith(QueryRuleEnum.LT.getValue())) {
+            str = str.substring(1);
+        }else if(str.indexOf(QueryGenerator.QUERY_COMMA_ESCAPE)>0) {
+            str = str.replaceAll("\\+\\+", SymbolConstant.COMMA);
+        }
+        if(dataBaseType==null){
+            dataBaseType = getDbType();
+        }
+        if(isString) {
+            if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType)){
+                return " N'"+str+"' ";
+            }else{
+                return " '"+str+"' ";
+            }
+        }else {
+            // 如果不是字符串 有一种特殊情况 popup调用都走这个逻辑 参数传递的可能是“‘admin’”这种格式的
+            if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
+                return " N"+str;
+            }
+            return value.toString();
+        }
+    }
+
+    private static String getInConditionValue(Object value,boolean isString) {
+        //update-begin-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
+        String[] temp = value.toString().split(",");
+        if(temp.length==0){
+            return "('')";
+        }
+        if(isString) {
+            List<String> res = new ArrayList<>();
+            for (String string : temp) {
+                if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
+                    res.add("N'"+string+"'");
+                }else{
+                    res.add("'"+string+"'");
+                }
+            }
+            return "("+String.join("," ,res)+")";
+        }else {
+            return "("+value.toString()+")";
+        }
+        //update-end-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
+    }
+
+    /**
+     * 先根据值判断 走左模糊还是右模糊
+     * 最后如果值不带任何标识(*或者%),则再根据ruleEnum判断
+     * @param value
+     * @param ruleEnum
+     * @return
+     */
+    private static String getLikeConditionValue(Object value, QueryRuleEnum ruleEnum) {
+        String str = value.toString().trim();
+        if(str.startsWith(SymbolConstant.ASTERISK) && str.endsWith(SymbolConstant.ASTERISK)) {
+            if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
+                return "N'%"+str.substring(1,str.length()-1)+"%'";
+            }else{
+                return "'%"+str.substring(1,str.length()-1)+"%'";
+            }
+        }else if(str.startsWith(SymbolConstant.ASTERISK)) {
+            if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
+                return "N'%"+str.substring(1)+"'";
+            }else{
+                return "'%"+str.substring(1)+"'";
+            }
+        }else if(str.endsWith(SymbolConstant.ASTERISK)) {
+            if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
+                return "N'"+str.substring(0,str.length()-1)+"%'";
+            }else{
+                return "'"+str.substring(0,str.length()-1)+"%'";
+            }
+        }else {
+            if(str.indexOf(SymbolConstant.PERCENT_SIGN)>=0) {
+                if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
+                    if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
+                        return "N"+str;
+                    }else{
+                        return "N"+"'"+str+"'";
+                    }
+                }else{
+                    if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
+                        return str;
+                    }else{
+                        return "'"+str+"'";
+                    }
+                }
+            }else {
+
+                //update-begin-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
+                // 走到这里说明 value不带有任何模糊查询的标识(*或者%)
+                if (ruleEnum == QueryRuleEnum.LEFT_LIKE) {
+                    if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
+                        return "N'%" + str + "'";
+                    } else {
+                        return "'%" + str + "'";
+                    }
+                } else if (ruleEnum == QueryRuleEnum.RIGHT_LIKE) {
+                    if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
+                        return "N'" + str + "%'";
+                    } else {
+                        return "'" + str + "%'";
+                    }
+                } else {
+                    if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
+                        return "N'%" + str + "%'";
+                    } else {
+                        return "'%" + str + "%'";
+                    }
+                }
+                //update-end-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
+
+            }
+        }
+    }
+
+    /**
+     * 获取系统数据库类型
+     */
+    private static String getDbType() {
+        return CommonUtils.getDatabaseType();
+    }
+    
+}

+ 40 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/ComboModel.java

@@ -0,0 +1,40 @@
+package org.jeecg.common.system.vo;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * @Description: 文档管理
+ * @author: jeecg-boot
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ComboModel implements Serializable {
+    private String id;
+    private String title;
+    /**文档管理 表单table默认选中*/
+    private boolean checked;
+    /**文档管理 表单table 用户账号*/
+    private String username;
+    /**文档管理 表单table 用户邮箱*/
+    private String email;
+    /**文档管理 表单table 角色编码*/
+    private String roleCode;
+
+    public ComboModel(){
+
+    };
+
+    public ComboModel(String id,String title,boolean checked,String username){
+        this.id = id;
+        this.title = title;
+        this.checked = false;
+        this.username = username;
+    };
+}

+ 70 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModel.java

@@ -0,0 +1,70 @@
+package org.jeecg.common.system.vo;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * @Description: 字典类
+ * @author: jeecg-boot
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DictModel implements Serializable{
+	private static final long serialVersionUID = 1L;
+
+	public DictModel() {
+	}
+	
+	public DictModel(String value, String text) {
+		this.value = value;
+		this.text = text;
+	}
+
+	public DictModel(String value, String text, String color) {
+		this.value = value;
+		this.text = text;
+		this.color = color;
+	}
+
+	/**
+	 * 字典value
+	 */
+	private String value;
+	/**
+	 * 字典文本
+	 */
+	private String text;
+	/**
+	 * 字典颜色
+	 */
+	private String color;
+
+	/**
+	 * 特殊用途: JgEditableTable
+	 * @return
+	 */
+	public String getTitle() {
+		return this.text;
+	}
+	/**
+	 * 特殊用途: vue3 Select组件
+	 */
+	public String getLabel() {
+		return this.text;
+	}
+
+
+	/**
+	 * 用于表单设计器 关联记录表数据存储
+	 * QQYUN-5595【表单设计器】他表字段 导入没有翻译
+	 */
+	private JSONObject jsonObject;
+
+}

+ 19 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModelMany.java

@@ -0,0 +1,19 @@
+package org.jeecg.common.system.vo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 查询多个字典时用到
+ * @author: jeecg-boot
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DictModelMany extends DictModel {
+
+    /**
+     * 字典code,根据多个字段code查询时才用到,用于区分不同的字典选项
+     */
+    private String dictCode;
+
+}

+ 35 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictQuery.java

@@ -0,0 +1,35 @@
+package org.jeecg.common.system.vo;
+
+import lombok.Data;
+
+/**
+ * 字典查询参数实体
+ * @author: jeecg-boot
+ */
+@Data
+public class DictQuery {
+    /**
+     * 表名
+     */
+    private String table;
+    /**
+     * 存储列
+     */
+    private String code;
+
+    /**
+     * 显示列
+     */
+    private String text;
+
+    /**
+     * 关键字查询
+     */
+    private String keyword;
+
+    /**
+     * 存储列的值 用于回显查询
+     */
+    private String codeValue;
+
+}

+ 58 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DynamicDataSourceModel.java

@@ -0,0 +1,58 @@
+package org.jeecg.common.system.vo;
+
+import lombok.Data;
+import org.springframework.beans.BeanUtils;
+
+/**
+ * @Description: 数据源
+ * @author: jeecg-boot
+ */
+@Data
+public class DynamicDataSourceModel {
+
+    public DynamicDataSourceModel() {
+
+    }
+
+    public DynamicDataSourceModel(Object dbSource) {
+        if (dbSource != null) {
+            BeanUtils.copyProperties(dbSource, this);
+        }
+    }
+
+    /**
+     * id
+     */
+    private java.lang.String id;
+    /**
+     * 数据源编码
+     */
+    private java.lang.String code;
+    /**
+     * 数据库类型
+     */
+    private java.lang.String dbType;
+    /**
+     * 驱动类
+     */
+    private java.lang.String dbDriver;
+    /**
+     * 数据源地址
+     */
+    private java.lang.String dbUrl;
+
+//    /**
+//     * 数据库名称
+//     */
+//    private java.lang.String dbName;
+
+    /**
+     * 用户名
+     */
+    private java.lang.String dbUsername;
+    /**
+     * 密码
+     */
+    private java.lang.String dbPassword;
+
+}

+ 131 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java

@@ -0,0 +1,131 @@
+package org.jeecg.common.system.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecg.common.desensitization.annotation.SensitiveField;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+/**
+ * <p>
+ * 在线用户信息
+ * </p>
+ *
+ * @Author scott
+ * @since 2018-12-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class LoginUser {
+
+	/**
+	 * 登录人id
+	 */
+	@SensitiveField
+	private String id;
+
+	/**
+	 * 登录人账号
+	 */
+	@SensitiveField
+	private String username;
+
+	/**
+	 * 登录人名字
+	 */
+	@SensitiveField
+	private String realname;
+
+	/**
+	 * 登录人密码
+	 */
+	@SensitiveField
+	private String password;
+
+     /**
+      * 当前登录部门code
+      */
+	@SensitiveField
+    private String orgCode;
+	/**
+	 * 头像
+	 */
+	@SensitiveField
+	private String avatar;
+
+	/**
+	 * 生日
+	 */
+	@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
+	@DateTimeFormat(pattern = "yyyy-MM-dd")
+	private Date birthday;
+
+	/**
+	 * 性别(1:男 2:女)
+	 */
+	private Integer sex;
+
+	/**
+	 * 电子邮件
+	 */
+	@SensitiveField
+	private String email;
+
+	/**
+	 * 电话
+	 */
+	@SensitiveField
+	private String phone;
+
+	/**
+	 * 状态(1:正常 2:冻结 )
+	 */
+	private Integer status;
+	
+	private Integer delFlag;
+	/**
+     * 同步工作流引擎1同步0不同步
+     */
+    private Integer activitiSync;
+
+	/**
+	 * 创建时间
+	 */
+	private Date createTime;
+
+	/**
+	 *  身份(1 普通员工 2 上级)
+	 */
+	private Integer userIdentity;
+
+	/**
+	 * 管理部门ids
+	 */
+	@SensitiveField
+	private String departIds;
+
+	/**
+	 * 职务,关联职务表
+	 */
+	@SensitiveField
+	private String post;
+
+	/**
+	 * 座机号
+	 */
+	@SensitiveField
+	private String telephone;
+
+	/** 多租户ids临时用,不持久化数据库(数据库字段不存在) */
+	@SensitiveField
+	private String relTenantIds;
+
+
+	/**设备id uniapp推送用*/
+	private String clientId;
+
+}

+ 32 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SelectTreeModel.java

@@ -0,0 +1,32 @@
+package org.jeecg.common.system.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 下拉树 model
+ *
+ * @author jeecg-boot
+ */
+@Data
+public class SelectTreeModel implements Serializable {
+
+    private String key;
+    private String title;
+    private String value;
+    /**
+     * 父Id
+     */
+    private String parentId;
+    /**
+     * 是否是叶节点
+     */
+    private boolean isLeaf;
+    /**
+     * 子节点
+     */
+    private List<SelectTreeModel> children;
+
+}

+ 50 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysCategoryModel.java

@@ -0,0 +1,50 @@
+package org.jeecg.common.system.vo;
+
+/**
+ * @Author qinfeng
+ * @Date 2020/2/19 12:01
+ * @Description:
+ * @Version 1.0
+ */
+public class SysCategoryModel {
+    /**主键*/
+    private java.lang.String id;
+    /**父级节点*/
+    private java.lang.String pid;
+    /**类型名称*/
+    private java.lang.String name;
+    /**类型编码*/
+    private java.lang.String code;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getPid() {
+        return pid;
+    }
+
+    public void setPid(String pid) {
+        this.pid = pid;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+}

+ 148 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysDepartModel.java

@@ -0,0 +1,148 @@
+package org.jeecg.common.system.vo;
+
+/**
+ * 部门机构model
+ * @author: lvdandan
+ */
+public class SysDepartModel {
+    /**ID*/
+    private String id;
+    /**父机构ID*/
+    private String parentId;
+    /**机构/部门名称*/
+    private String departName;
+    /**英文名*/
+    private String departNameEn;
+    /**缩写*/
+    private String departNameAbbr;
+    /**排序*/
+    private Integer departOrder;
+    /**描述*/
+    private String description;
+    /**机构类别 1组织机构,2岗位*/
+    private String orgCategory;
+    /**机构类型*/
+    private String orgType;
+    /**机构编码*/
+    private String orgCode;
+    /**手机号*/
+    private String mobile;
+    /**传真*/
+    private String fax;
+    /**地址*/
+    private String address;
+    /**备注*/
+    private String memo;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(String parentId) {
+        this.parentId = parentId;
+    }
+
+    public String getDepartName() {
+        return departName;
+    }
+
+    public void setDepartName(String departName) {
+        this.departName = departName;
+    }
+
+    public String getDepartNameEn() {
+        return departNameEn;
+    }
+
+    public void setDepartNameEn(String departNameEn) {
+        this.departNameEn = departNameEn;
+    }
+
+    public String getDepartNameAbbr() {
+        return departNameAbbr;
+    }
+
+    public void setDepartNameAbbr(String departNameAbbr) {
+        this.departNameAbbr = departNameAbbr;
+    }
+
+    public Integer getDepartOrder() {
+        return departOrder;
+    }
+
+    public void setDepartOrder(Integer departOrder) {
+        this.departOrder = departOrder;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getOrgCategory() {
+        return orgCategory;
+    }
+
+    public void setOrgCategory(String orgCategory) {
+        this.orgCategory = orgCategory;
+    }
+
+    public String getOrgType() {
+        return orgType;
+    }
+
+    public void setOrgType(String orgType) {
+        this.orgType = orgType;
+    }
+
+    public String getOrgCode() {
+        return orgCode;
+    }
+
+    public void setOrgCode(String orgCode) {
+        this.orgCode = orgCode;
+    }
+
+    public String getMobile() {
+        return mobile;
+    }
+
+    public void setMobile(String mobile) {
+        this.mobile = mobile;
+    }
+
+    public String getFax() {
+        return fax;
+    }
+
+    public void setFax(String fax) {
+        this.fax = fax;
+    }
+
+    public String getAddress() {
+        return address;
+    }
+
+    public void setAddress(String address) {
+        this.address = address;
+    }
+
+    public String getMemo() {
+        return memo;
+    }
+
+    public void setMemo(String memo) {
+        this.memo = memo;
+    }
+}

+ 144 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysPermissionDataRuleModel.java

@@ -0,0 +1,144 @@
+package org.jeecg.common.system.vo;
+
+import java.util.Date;
+
+/**
+ * <p>
+ * 菜单权限规则表
+ * </p>
+ *
+ * @Author huangzhilin
+ * @since 2019-03-29
+ */
+public class SysPermissionDataRuleModel {
+
+    /**
+     * id
+     */
+    private String id;
+
+    /**
+     * 对应的菜单id
+     */
+    private String permissionId;
+
+    /**
+     * 规则名称
+     */
+    private String ruleName;
+
+    /**
+     * 字段
+     */
+    private String ruleColumn;
+
+    /**
+     * 条件
+     */
+    private String ruleConditions;
+
+    /**
+     * 规则值
+     */
+    private String ruleValue;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 创建人
+     */
+    private String createBy;
+
+    /**
+     * 修改时间
+     */
+    private Date updateTime;
+
+    /**
+     * 修改人
+     */
+    private String updateBy;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getPermissionId() {
+        return permissionId;
+    }
+
+    public void setPermissionId(String permissionId) {
+        this.permissionId = permissionId;
+    }
+
+    public String getRuleName() {
+        return ruleName;
+    }
+
+    public void setRuleName(String ruleName) {
+        this.ruleName = ruleName;
+    }
+
+    public String getRuleColumn() {
+        return ruleColumn;
+    }
+
+    public void setRuleColumn(String ruleColumn) {
+        this.ruleColumn = ruleColumn;
+    }
+
+    public String getRuleConditions() {
+        return ruleConditions;
+    }
+
+    public void setRuleConditions(String ruleConditions) {
+        this.ruleConditions = ruleConditions;
+    }
+
+    public String getRuleValue() {
+        return ruleValue;
+    }
+
+    public void setRuleValue(String ruleValue) {
+        this.ruleValue = ruleValue;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public String getCreateBy() {
+        return createBy;
+    }
+
+    public void setCreateBy(String createBy) {
+        this.createBy = createBy;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getUpdateBy() {
+        return updateBy;
+    }
+
+    public void setUpdateBy(String updateBy) {
+        this.updateBy = updateBy;
+    }
+}

+ 71 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysUserCacheInfo.java

@@ -0,0 +1,71 @@
+package org.jeecg.common.system.vo;
+
+import org.jeecg.common.util.DateUtils;
+
+import java.util.List;
+
+/**
+ * @Description: 用户缓存信息
+ * @author: jeecg-boot
+ */
+public class SysUserCacheInfo {
+	
+	private String sysUserCode;
+	
+	private String sysUserName;
+	
+	private String sysOrgCode;
+	
+	private List<String> sysMultiOrgCode;
+	
+	private boolean oneDepart;
+	
+	public boolean isOneDepart() {
+		return oneDepart;
+	}
+
+	public void setOneDepart(boolean oneDepart) {
+		this.oneDepart = oneDepart;
+	}
+
+	public String getSysDate() {
+		return DateUtils.formatDate();
+	}
+
+	public String getSysTime() {
+		return DateUtils.now();
+	}
+
+	public String getSysUserCode() {
+		return sysUserCode;
+	}
+
+	public void setSysUserCode(String sysUserCode) {
+		this.sysUserCode = sysUserCode;
+	}
+
+	public String getSysUserName() {
+		return sysUserName;
+	}
+
+	public void setSysUserName(String sysUserName) {
+		this.sysUserName = sysUserName;
+	}
+
+	public String getSysOrgCode() {
+		return sysOrgCode;
+	}
+
+	public void setSysOrgCode(String sysOrgCode) {
+		this.sysOrgCode = sysOrgCode;
+	}
+
+	public List<String> getSysMultiOrgCode() {
+		return sysMultiOrgCode;
+	}
+
+	public void setSysMultiOrgCode(List<String> sysMultiOrgCode) {
+		this.sysMultiOrgCode = sysMultiOrgCode;
+	}
+
+}

+ 57 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/UserAccountInfo.java

@@ -0,0 +1,57 @@
+package org.jeecg.common.system.vo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecg.common.desensitization.annotation.SensitiveField;
+
+/**
+ * <p>
+ * 在线用户信息
+ * </p>
+ *
+ * @Author scott
+ * @since 2023-08-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class UserAccountInfo {
+
+    /**
+     * 登录人id
+     */
+    private String id;
+
+    /**
+     * 登录人账号
+     */
+    private String username;
+
+    /**
+     * 登录人名字
+     */
+    private String realname;
+
+    /**
+     * 电子邮件
+     */
+    private String email;
+
+    /**
+     * 头像
+     */
+    @SensitiveField
+    private String avatar;
+
+    /**
+     * 同步工作流引擎1同步0不同步
+     */
+    private Integer activitiSync;
+
+    /**
+     * 电话
+     */
+    @SensitiveField
+    private String phone;
+}

+ 470 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java

@@ -0,0 +1,470 @@
+package org.jeecg.common.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.DataBaseConstant;
+import org.jeecg.common.constant.ServiceNameConstants;
+import org.jeecg.common.constant.SymbolConstant;
+import org.jeecg.common.exception.JeecgBootException;
+import org.jeecg.common.util.oss.OssBootUtil;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.sql.DataSource;
+import java.io.File;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @Description: 通用工具
+ * @author: jeecg-boot
+ */
+@Slf4j
+public class CommonUtils {
+
+    /**
+     * 中文正则
+     */
+    private static Pattern ZHONGWEN_PATTERN = Pattern.compile("[\u4e00-\u9fa5]");
+
+    /**
+     * 文件名 正则字符串
+     * 文件名支持的字符串:字母数字中文.-_()() 除此之外的字符将被删除
+     */
+    private static String FILE_NAME_REGEX = "[^A-Za-z\\.\\(\\)\\-()\\_0-9\\u4e00-\\u9fa5]";
+
+    /**
+     * 判断文件名是否带盘符,重新处理
+     *
+     * @param fileName
+     * @return
+     */
+    public static String getFileName(String fileName) {
+        // 判断是否带有盘符信息
+        // Check for Unix-style path
+        int unixSep = fileName.lastIndexOf('/');
+        // Check for Windows-style path
+        int winSep = fileName.lastIndexOf('\\');
+        // Cut off at latest possible point
+        int pos = (winSep > unixSep ? winSep : unixSep);
+        if (pos != -1) {
+            // Any sort of path separator found...
+            fileName = fileName.substring(pos + 1);
+        }
+        // 替换上传文件名字的特殊字符
+        fileName = fileName
+                .replace("=", "")
+                .replace(",", "")
+                .replace("&", "")
+                .replace("#", "")
+                .replace("“", "")
+                .replace("”", "")
+                .replaceAll("\\s", "")
+                .replaceAll(FILE_NAME_REGEX, "");
+        return fileName;
+    }
+
+    /**
+     * java 判断字符串里是否包含中文字符
+     *
+     * @param str
+     * @return
+     */
+    public static boolean ifContainChinese(String str) {
+        if (str.getBytes().length == str.length()) {
+            return false;
+        } else {
+            Matcher m = ZHONGWEN_PATTERN.matcher(str);
+            if (m.find()) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * 统一全局上传
+     *
+     * @Return: java.lang.String
+     */
+    public static String upload(MultipartFile file, String bizPath, String uploadType) {
+        String url = "";
+        try {
+            if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
+                url = MinioUtil.upload(file, bizPath);
+            } else {
+                url = OssBootUtil.upload(file, bizPath);
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            throw new JeecgBootException(e.getMessage());
+        }
+        return url;
+    }
+
+    /**
+     * 本地文件上传
+     *
+     * @param mf      文件
+     * @param bizPath 自定义路径
+     * @return
+     */
+    public static String uploadLocal(MultipartFile mf, String bizPath, String uploadpath) {
+        try {
+            String fileName = null;
+            File file = new File(uploadpath + File.separator + bizPath + File.separator);
+            if (!file.exists()) {
+                // 创建文件根目录
+                file.mkdirs();
+            }
+            // 获取文件名
+            String orgName = mf.getOriginalFilename();
+            orgName = CommonUtils.getFileName(orgName);
+            if (orgName.indexOf(SymbolConstant.SPOT) != -1) {
+                fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
+            } else {
+                fileName = orgName + "_" + System.currentTimeMillis();
+            }
+            String savePath = file.getPath() + File.separator + fileName;
+            File savefile = new File(savePath);
+            FileCopyUtils.copy(mf.getBytes(), savefile);
+            String dbpath = null;
+            if (oConvertUtils.isNotEmpty(bizPath)) {
+                dbpath = bizPath + File.separator + fileName;
+            } else {
+                dbpath = fileName;
+            }
+            if (dbpath.contains(SymbolConstant.DOUBLE_BACKSLASH)) {
+                dbpath = dbpath.replace("\\", "/");
+            }
+            return dbpath;
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return "";
+    }
+
+    /**
+     * 统一全局上传 带桶
+     *
+     * @Return: java.lang.String
+     */
+    public static String upload(MultipartFile file, String bizPath, String uploadType, String customBucket) {
+        String url = "";
+        try {
+            if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
+                url = MinioUtil.upload(file, bizPath, customBucket);
+            } else {
+                url = OssBootUtil.upload(file, bizPath, customBucket);
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return url;
+    }
+
+    /** 当前系统数据库类型 */
+    private static String DB_TYPE = "";
+    private static DbType dbTypeEnum = null;
+
+    /**
+     * 全局获取平台数据库类型(作废了)
+     *
+     * @return
+     */
+    @Deprecated
+    public static String getDatabaseType() {
+        if (oConvertUtils.isNotEmpty(DB_TYPE)) {
+            return DB_TYPE;
+        }
+        DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
+        try {
+            return getDatabaseTypeByDataSource(dataSource);
+        } catch (SQLException e) {
+            // e.printStackTrace();
+            log.warn(e.getMessage(), e);
+            return "";
+        }
+    }
+
+    /**
+     * 全局获取平台数据库类型(对应mybaisPlus枚举)
+     *
+     * @return
+     */
+    public static DbType getDatabaseTypeEnum() {
+        if (oConvertUtils.isNotEmpty(dbTypeEnum)) {
+            return dbTypeEnum;
+        }
+        try {
+            DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
+            dbTypeEnum = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
+            return dbTypeEnum;
+        } catch (SQLException e) {
+            log.warn(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 根据数据源key获取DataSourceProperty
+     *
+     * @param sourceKey
+     * @return
+     */
+    public static DataSourceProperty getDataSourceProperty(String sourceKey) {
+        DynamicDataSourceProperties prop = SpringContextUtils.getApplicationContext().getBean(DynamicDataSourceProperties.class);
+        Map<String, DataSourceProperty> map = prop.getDatasource();
+        DataSourceProperty db = (DataSourceProperty) map.get(sourceKey);
+        return db;
+    }
+
+    /**
+     * 根据sourceKey 获取数据源连接
+     *
+     * @param sourceKey
+     * @return
+     * @throws SQLException
+     */
+    public static Connection getDataSourceConnect(String sourceKey) throws SQLException {
+        if (oConvertUtils.isEmpty(sourceKey)) {
+            sourceKey = "master";
+        }
+        DynamicDataSourceProperties prop = SpringContextUtils.getApplicationContext().getBean(DynamicDataSourceProperties.class);
+        Map<String, DataSourceProperty> map = prop.getDatasource();
+        DataSourceProperty db = (DataSourceProperty) map.get(sourceKey);
+        if (db == null) {
+            return null;
+        }
+        DriverManagerDataSource ds = new DriverManagerDataSource();
+        ds.setDriverClassName(db.getDriverClassName());
+        ds.setUrl(db.getUrl());
+        ds.setUsername(db.getUsername());
+        ds.setPassword(db.getPassword());
+        return ds.getConnection();
+    }
+
+    /**
+     * 获取数据库类型
+     *
+     * @param dataSource
+     * @return
+     * @throws SQLException
+     */
+    private static String getDatabaseTypeByDataSource(DataSource dataSource) throws SQLException {
+        if ("".equals(DB_TYPE)) {
+            Connection connection = dataSource.getConnection();
+            try {
+                DatabaseMetaData md = connection.getMetaData();
+                String dbType = md.getDatabaseProductName().toUpperCase();
+                String sqlserver = "SQL SERVER";
+                if (dbType.indexOf(DataBaseConstant.DB_TYPE_MYSQL) >= 0) {
+                    DB_TYPE = DataBaseConstant.DB_TYPE_MYSQL;
+                } else if (dbType.indexOf(DataBaseConstant.DB_TYPE_ORACLE) >= 0 || dbType.indexOf(DataBaseConstant.DB_TYPE_DM) >= 0) {
+                    DB_TYPE = DataBaseConstant.DB_TYPE_ORACLE;
+                } else if (dbType.indexOf(DataBaseConstant.DB_TYPE_SQLSERVER) >= 0 || dbType.indexOf(sqlserver) >= 0) {
+                    DB_TYPE = DataBaseConstant.DB_TYPE_SQLSERVER;
+                } else if (dbType.indexOf(DataBaseConstant.DB_TYPE_POSTGRESQL) >= 0 || dbType.indexOf(DataBaseConstant.DB_TYPE_KINGBASEES) >= 0) {
+                    DB_TYPE = DataBaseConstant.DB_TYPE_POSTGRESQL;
+                } else if (dbType.indexOf(DataBaseConstant.DB_TYPE_MARIADB) >= 0) {
+                    DB_TYPE = DataBaseConstant.DB_TYPE_MARIADB;
+                } else {
+                    log.error("数据库类型:[" + dbType + "]不识别!");
+                    // throw new JeecgBootException("数据库类型:["+dbType+"]不识别!");
+                }
+            } catch (Exception e) {
+                log.error(e.getMessage(), e);
+            } finally {
+                connection.close();
+            }
+        }
+        return DB_TYPE;
+
+    }
+
+    /**
+     * 获取服务器地址
+     *
+     * @param request
+     * @return
+     */
+    public static String getBaseUrl(HttpServletRequest request) {
+        // 1.【兼容】兼容微服务下的 base path-------
+        String xGatewayBasePath = request.getHeader(ServiceNameConstants.X_GATEWAY_BASE_PATH);
+        if (oConvertUtils.isNotEmpty(xGatewayBasePath)) {
+            log.info("x_gateway_base_path = " + xGatewayBasePath);
+            return xGatewayBasePath;
+        }
+        // 2.【兼容】SSL认证之后,request.getScheme()获取不到https的问题
+        // https://blog.csdn.net/weixin_34376986/article/details/89767950
+        String scheme = request.getHeader(CommonConstant.X_FORWARDED_SCHEME);
+        if (oConvertUtils.isEmpty(scheme)) {
+            scheme = request.getScheme();
+        }
+
+        // 3.常规操作
+        String serverName = request.getServerName();
+        int serverPort = request.getServerPort();
+        String contextPath = request.getContextPath();
+
+        // 返回 host domain
+        String baseDomainPath = null;
+        // update-begin---author:wangshuai---date:2024-03-15---for:【QQYUN-8561】企业微信登陆请求接口设置上下文不一致,导致接口404---
+        int httpPort = 80;
+        int httpsPort = 443;
+        if (httpPort == serverPort || httpsPort == serverPort) {
+            // update-end---author:wangshuai---date:2024-03-15---for:【QQYUN-8561】企业微信登陆请求接口设置上下文不一致,导致接口404---~
+            baseDomainPath = scheme + "://" + serverName + contextPath;
+        } else {
+            baseDomainPath = scheme + "://" + serverName + ":" + serverPort + contextPath;
+        }
+        log.debug("-----Common getBaseUrl----- : " + baseDomainPath);
+        return baseDomainPath;
+    }
+
+    /**
+     * 递归合并 fastJSON 对象
+     *
+     * @param target  目标对象
+     * @param sources 来源对象,允许多个,优先级从左到右,最右侧的优先级最高
+     */
+    public static JSONObject mergeJSON(JSONObject target, JSONObject... sources) {
+        for (JSONObject source : sources) {
+            CommonUtils.mergeJSON(target, source);
+        }
+        return target;
+    }
+
+    /**
+     * 递归合并 fastJSON 对象
+     *
+     * @param target 目标对象
+     * @param source 来源对象
+     */
+    public static JSONObject mergeJSON(JSONObject target, JSONObject source) {
+        for (String key : source.keySet()) {
+            Object sourceItem = source.get(key);
+            // 是否是 JSONObject
+            if (sourceItem instanceof Map) {
+                // target中存在此key
+                if (target.containsKey(key)) {
+                    // 两个都是 JSONObject,继续合并
+                    if (target.get(key) instanceof Map) {
+                        CommonUtils.mergeJSON(target.getJSONObject(key), source.getJSONObject(key));
+                        continue;
+                    }
+                }
+            }
+            // target不存在此key,或不是 JSONObject,则覆盖
+            target.put(key, sourceItem);
+        }
+        return target;
+    }
+
+    /**
+     * 将list集合以分割符的方式进行分割
+     *
+     * @param list      String类型的集合文本
+     * @param separator 分隔符
+     * @return
+     */
+    public static String getSplitText(List<String> list, String separator) {
+        if (null != list && list.size() > 0) {
+            return StringUtils.join(list, separator);
+        }
+        return "";
+    }
+
+    /**
+     * 通过table的条件SQL
+     *
+     * @param tableSql sys_user where name = '1212'
+     * @return name = '1212'
+     */
+    public static String getFilterSqlByTableSql(String tableSql) {
+        if (oConvertUtils.isEmpty(tableSql)) {
+            return null;
+        }
+
+        if (tableSql.toLowerCase().indexOf(DataBaseConstant.SQL_WHERE) > 0) {
+            String[] arr = tableSql.split(" (?i)where ");
+            if (arr != null && oConvertUtils.isNotEmpty(arr[1])) {
+                return arr[1];
+            }
+        }
+        return "";
+    }
+
+    /**
+     * 通过table获取表名
+     *
+     * @param tableSql sys_user where name = '1212'
+     * @return sys_user
+     */
+    public static String getTableNameByTableSql(String tableSql) {
+        if (oConvertUtils.isEmpty(tableSql)) {
+            return null;
+        }
+
+        if (tableSql.toLowerCase().indexOf(DataBaseConstant.SQL_WHERE) > 0) {
+            String[] arr = tableSql.split(" (?i)where ");
+            return arr[0].trim();
+        } else {
+            return tableSql;
+        }
+    }
+
+    /**
+     * 判断两个数组是否存在交集
+     *
+     * @param set1
+     * @param arr2
+     * @return
+     */
+    public static boolean hasIntersection(Set<String> set1, String[] arr2) {
+        if (set1 == null) {
+            return false;
+        }
+
+        if (set1.size() > 0) {
+            for (String str : arr2) {
+                if (set1.contains(str)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 输出info日志,会捕获异常,防止因为日志问题导致程序异常
+     *
+     * @param msg
+     * @param objects
+     */
+    public static void logInfo(String msg, Object... objects) {
+        try {
+            log.info(msg, objects);
+        } catch (Exception e) {
+            log.warn("{} —— {}", msg, e.getMessage());
+        }
+    }
+
+}

+ 762 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java

@@ -0,0 +1,762 @@
+package org.jeecg.common.util;
+
+import org.jeecg.common.constant.SymbolConstant;
+import org.springframework.util.StringUtils;
+
+import java.beans.PropertyEditorSupport;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+/**
+ * 类描述:时间操作定义类
+ *
+ * @Author: 张代浩
+ * @Date:2012-12-8 12:15:03
+ * @Version 1.0
+ */
+public class DateUtils extends PropertyEditorSupport {
+
+    public static ThreadLocal<SimpleDateFormat> date_sdf = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyy-MM-dd");
+        }
+    };
+    public static ThreadLocal<SimpleDateFormat> yyyyMMdd = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyyMMdd");
+        }
+    };
+    public static ThreadLocal<SimpleDateFormat> date_sdf_wz = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyy年MM月dd日");
+        }
+    };
+    public static ThreadLocal<SimpleDateFormat> time_sdf = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyy-MM-dd HH:mm");
+        }
+    };
+    public static ThreadLocal<SimpleDateFormat> yyyymmddhhmmss = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyyMMddHHmmss");
+        }
+    };
+    public static ThreadLocal<SimpleDateFormat> short_time_sdf = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("HH:mm");
+        }
+    };
+    public static ThreadLocal<SimpleDateFormat> datetimeFormat = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        }
+    };
+
+    /**
+     * 以毫秒表示的时间
+     */
+    private static final long DAY_IN_MILLIS = 24 * 3600 * 1000;
+    private static final long HOUR_IN_MILLIS = 3600 * 1000;
+    private static final long MINUTE_IN_MILLIS = 60 * 1000;
+    private static final long SECOND_IN_MILLIS = 1000;
+
+    /**
+     * 指定模式的时间格式
+     * @param pattern
+     * @return
+     */
+    private static SimpleDateFormat getSdFormat(String pattern) {
+        return new SimpleDateFormat(pattern);
+    }
+
+    /**
+     * 当前日历,这里用中国时间表示
+     *
+     * @return 以当地时区表示的系统当前日历
+     */
+    public static Calendar getCalendar() {
+        return Calendar.getInstance();
+    }
+
+    /**
+     * 指定毫秒数表示的日历
+     *
+     * @param millis 毫秒数
+     * @return 指定毫秒数表示的日历
+     */
+    public static Calendar getCalendar(long millis) {
+        Calendar cal = Calendar.getInstance();
+        // --------------------cal.setTimeInMillis(millis);
+        cal.setTime(new Date(millis));
+        return cal;
+    }
+
+    // ////////////////////////////////////////////////////////////////////////////
+    // getDate
+    // 各种方式获取的Date
+    // ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * 当前日期
+     *
+     * @return 系统当前时间
+     */
+    public static Date getDate() {
+        return new Date();
+    }
+
+    /**
+     * 指定毫秒数表示的日期
+     *
+     * @param millis 毫秒数
+     * @return 指定毫秒数表示的日期
+     */
+    public static Date getDate(long millis) {
+        return new Date(millis);
+    }
+
+    /**
+     * 时间戳转换为字符串
+     *
+     * @param time
+     * @return
+     */
+    public static String timestamptoStr(Timestamp time) {
+        Date date = null;
+        if (null != time) {
+            date = new Date(time.getTime());
+        }
+        return date2Str(date_sdf.get());
+    }
+
+    /**
+     * 字符串转换时间戳
+     *
+     * @param str
+     * @return
+     */
+    public static Timestamp str2Timestamp(String str) {
+        Date date = str2Date(str, date_sdf.get());
+        return new Timestamp(date.getTime());
+    }
+
+    /**
+     * 字符串转换成日期
+     *
+     * @param str
+     * @param sdf
+     * @return
+     */
+    public static Date str2Date(String str, SimpleDateFormat sdf) {
+        if (null == str || "".equals(str)) {
+            return null;
+        }
+        Date date = null;
+        try {
+            date = sdf.parse(str);
+            return date;
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 日期转换为字符串
+     *
+     * @param dateSdf 日期格式
+     * @return 字符串
+     */
+    public static String date2Str(SimpleDateFormat dateSdf) {
+        synchronized (dateSdf) {
+            Date date = getDate();
+            if (null == date) {
+                return null;
+            }
+            return dateSdf.format(date);
+        }
+    }
+
+    /**
+     * 格式化时间
+     *
+     * @param date
+     * @param format
+     * @return
+     */
+    public static String dateformat(String date, String format) {
+        SimpleDateFormat sformat = new SimpleDateFormat(format);
+        Date nowDate = null;
+        try {
+            nowDate = sformat.parse(date);
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        return sformat.format(nowDate);
+    }
+
+    /**
+     * 日期转换为字符串
+     *
+     * @param date     日期
+     * @param dateSdf 日期格式
+     * @return 字符串
+     */
+    public static String date2Str(Date date, SimpleDateFormat dateSdf) {
+        synchronized (dateSdf) {
+            if (null == date) {
+                return null;
+            }
+            return dateSdf.format(date);
+        }
+    }
+
+    /**
+     * 日期转换为字符串
+     *
+     * @param format 日期格式
+     * @return 字符串
+     */
+    public static String getDate(String format) {
+        Date date = new Date();
+        if (null == date) {
+            return null;
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        return sdf.format(date);
+    }
+
+    /**
+     * 指定毫秒数的时间戳
+     *
+     * @param millis 毫秒数
+     * @return 指定毫秒数的时间戳
+     */
+    public static Timestamp getTimestamp(long millis) {
+        return new Timestamp(millis);
+    }
+
+    /**
+     * 以字符形式表示的时间戳
+     *
+     * @param time 毫秒数
+     * @return 以字符形式表示的时间戳
+     */
+    public static Timestamp getTimestamp(String time) {
+        return new Timestamp(Long.parseLong(time));
+    }
+
+    /**
+     * 系统当前的时间戳
+     *
+     * @return 系统当前的时间戳
+     */
+    public static Timestamp getTimestamp() {
+        return new Timestamp(System.currentTimeMillis());
+    }
+
+    /**
+     * 当前时间,格式 yyyy-MM-dd HH:mm:ss
+     *
+     * @return 当前时间的标准形式字符串
+     */
+    public static String now() {
+        return datetimeFormat.get().format(getCalendar().getTime());
+    }
+
+    /**
+     * 指定日期的时间戳
+     *
+     * @param date 指定日期
+     * @return 指定日期的时间戳
+     */
+    public static Timestamp getTimestamp(Date date) {
+        return new Timestamp(date.getTime());
+    }
+
+    /**
+     * 指定日历的时间戳
+     *
+     * @param cal 指定日历
+     * @return 指定日历的时间戳
+     */
+    public static Timestamp getCalendarTimestamp(Calendar cal) {
+        // ---------------------return new Timestamp(cal.getTimeInMillis());
+        return new Timestamp(cal.getTime().getTime());
+    }
+
+    public static Timestamp gettimestamp() {
+        Date dt = new Date();
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String nowTime = df.format(dt);
+        java.sql.Timestamp buydate = java.sql.Timestamp.valueOf(nowTime);
+        return buydate;
+    }
+
+    // ////////////////////////////////////////////////////////////////////////////
+    // getMillis
+    // 各种方式获取的Millis
+    // ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * 系统时间的毫秒数
+     *
+     * @return 系统时间的毫秒数
+     */
+    public static long getMillis() {
+        return System.currentTimeMillis();
+    }
+
+    /**
+     * 指定日历的毫秒数
+     *
+     * @param cal 指定日历
+     * @return 指定日历的毫秒数
+     */
+    public static long getMillis(Calendar cal) {
+        // --------------------return cal.getTimeInMillis();
+        return cal.getTime().getTime();
+    }
+
+    /**
+     * 指定日期的毫秒数
+     *
+     * @param date 指定日期
+     * @return 指定日期的毫秒数
+     */
+    public static long getMillis(Date date) {
+        return date.getTime();
+    }
+
+    /**
+     * 指定时间戳的毫秒数
+     *
+     * @param ts 指定时间戳
+     * @return 指定时间戳的毫秒数
+     */
+    public static long getMillis(Timestamp ts) {
+        return ts.getTime();
+    }
+
+    // ////////////////////////////////////////////////////////////////////////////
+    // formatDate
+    // 将日期按照一定的格式转化为字符串
+    // ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * 默认方式表示的系统当前日期,具体格式:年-月-日
+     *
+     * @return 默认日期按“年-月-日“格式显示
+     */
+    public static String formatDate() {
+        return date_sdf.get().format(getCalendar().getTime());
+    }
+
+    /**
+     * 默认方式表示的系统当前日期,具体格式:yyyy-MM-dd HH:mm:ss
+     *
+     * @return 默认日期按“yyyy-MM-dd HH:mm:ss“格式显示
+     */
+    public static String formatDateTime() {
+        return datetimeFormat.get().format(getCalendar().getTime());
+    }
+
+    /**
+     * 获取时间字符串
+     */
+    public static String getDataString(SimpleDateFormat formatstr) {
+        synchronized (formatstr) {
+            return formatstr.format(getCalendar().getTime());
+        }
+    }
+
+    /**
+     * 指定日期的默认显示,具体格式:年-月-日
+     *
+     * @param cal 指定的日期
+     * @return 指定日期按“年-月-日“格式显示
+     */
+    public static String formatDate(Calendar cal) {
+        return date_sdf.get().format(cal.getTime());
+    }
+
+    /**
+     * 指定日期的默认显示,具体格式:年-月-日
+     *
+     * @param date 指定的日期
+     * @return 指定日期按“年-月-日“格式显示
+     */
+    public static String formatDate(Date date) {
+        return date_sdf.get().format(date);
+    }
+
+    /**
+     * 指定毫秒数表示日期的默认显示,具体格式:年-月-日
+     *
+     * @param millis 指定的毫秒数
+     * @return 指定毫秒数表示日期按“年-月-日“格式显示
+     */
+    public static String formatDate(long millis) {
+        return date_sdf.get().format(new Date(millis));
+    }
+
+    /**
+     * 默认日期按指定格式显示
+     *
+     * @param pattern 指定的格式
+     * @return 默认日期按指定格式显示
+     */
+    public static String formatDate(String pattern) {
+        return getSdFormat(pattern).format(getCalendar().getTime());
+    }
+
+    /**
+     * 指定日期按指定格式显示
+     *
+     * @param cal     指定的日期
+     * @param pattern 指定的格式
+     * @return 指定日期按指定格式显示
+     */
+    public static String formatDate(Calendar cal, String pattern) {
+        return getSdFormat(pattern).format(cal.getTime());
+    }
+
+    /**
+     * 指定日期按指定格式显示
+     *
+     * @param date    指定的日期
+     * @param pattern 指定的格式
+     * @return 指定日期按指定格式显示
+     */
+    public static String formatDate(Date date, String pattern) {
+        return getSdFormat(pattern).format(date);
+    }
+
+    // ////////////////////////////////////////////////////////////////////////////
+    // formatTime
+    // 将日期按照一定的格式转化为字符串
+    // ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * 默认方式表示的系统当前日期,具体格式:年-月-日 时:分
+     *
+     * @return 默认日期按“年-月-日 时:分“格式显示
+     */
+    public static String formatTime() {
+        return time_sdf.get().format(getCalendar().getTime());
+    }
+
+    /**
+     * 指定毫秒数表示日期的默认显示,具体格式:年-月-日 时:分
+     *
+     * @param millis 指定的毫秒数
+     * @return 指定毫秒数表示日期按“年-月-日 时:分“格式显示
+     */
+    public static String formatTime(long millis) {
+        return time_sdf.get().format(new Date(millis));
+    }
+
+    /**
+     * 指定日期的默认显示,具体格式:年-月-日 时:分
+     *
+     * @param cal 指定的日期
+     * @return 指定日期按“年-月-日 时:分“格式显示
+     */
+    public static String formatTime(Calendar cal) {
+        return time_sdf.get().format(cal.getTime());
+    }
+
+    /**
+     * 指定日期的默认显示,具体格式:年-月-日 时:分
+     *
+     * @param date 指定的日期
+     * @return 指定日期按“年-月-日 时:分“格式显示
+     */
+    public static String formatTime(Date date) {
+        return time_sdf.get().format(date);
+    }
+
+    // ////////////////////////////////////////////////////////////////////////////
+    // formatShortTime
+    // 将日期按照一定的格式转化为字符串
+    // ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * 默认方式表示的系统当前日期,具体格式:时:分
+     *
+     * @return 默认日期按“时:分“格式显示
+     */
+    public static String formatShortTime() {
+        return short_time_sdf.get().format(getCalendar().getTime());
+    }
+
+    /**
+     * 指定毫秒数表示日期的默认显示,具体格式:时:分
+     *
+     * @param millis 指定的毫秒数
+     * @return 指定毫秒数表示日期按“时:分“格式显示
+     */
+    public static String formatShortTime(long millis) {
+        return short_time_sdf.get().format(new Date(millis));
+    }
+
+    /**
+     * 指定日期的默认显示,具体格式:时:分
+     *
+     * @param cal 指定的日期
+     * @return 指定日期按“时:分“格式显示
+     */
+    public static String formatShortTime(Calendar cal) {
+        return short_time_sdf.get().format(cal.getTime());
+    }
+
+    /**
+     * 指定日期的默认显示,具体格式:时:分
+     *
+     * @param date 指定的日期
+     * @return 指定日期按“时:分“格式显示
+     */
+    public static String formatShortTime(Date date) {
+        return short_time_sdf.get().format(date);
+    }
+
+    // ////////////////////////////////////////////////////////////////////////////
+    // parseDate
+    // parseCalendar
+    // parseTimestamp
+    // 将字符串按照一定的格式转化为日期或时间
+    // ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * 根据指定的格式将字符串转换成Date 如输入:2003-11-19 11:20:20将按照这个转成时间
+     *
+     * @param src     将要转换的原始字符窜
+     * @param pattern 转换的匹配格式
+     * @return 如果转换成功则返回转换后的日期
+     * @throws ParseException
+     */
+    public static Date parseDate(String src, String pattern) throws ParseException {
+        return getSdFormat(pattern).parse(src);
+
+    }
+
+    /**
+     * 根据指定的格式将字符串转换成Date 如输入:2003-11-19 11:20:20将按照这个转成时间
+     *
+     * @param src     将要转换的原始字符窜
+     * @param pattern 转换的匹配格式
+     * @return 如果转换成功则返回转换后的日期
+     * @throws ParseException
+     */
+    public static Calendar parseCalendar(String src, String pattern) throws ParseException {
+
+        Date date = parseDate(src, pattern);
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(date);
+        return cal;
+    }
+
+    public static String formatAddDate(String src, String pattern, int amount) throws ParseException {
+        Calendar cal;
+        cal = parseCalendar(src, pattern);
+        cal.add(Calendar.DATE, amount);
+        return formatDate(cal);
+    }
+
+    /**
+     * 根据指定的格式将字符串转换成Date 如输入:2003-11-19 11:20:20将按照这个转成时间
+     *
+     * @param src     将要转换的原始字符窜
+     * @param pattern 转换的匹配格式
+     * @return 如果转换成功则返回转换后的时间戳
+     * @throws ParseException
+     */
+    public static Timestamp parseTimestamp(String src, String pattern) throws ParseException {
+        Date date = parseDate(src, pattern);
+        return new Timestamp(date.getTime());
+    }
+
+    // ////////////////////////////////////////////////////////////////////////////
+    // dateDiff
+    // 计算两个日期之间的差值
+    // ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * 计算两个时间之间的差值,根据标志的不同而不同
+     *
+     * @param flag   计算标志,表示按照年/月/日/时/分/秒等计算
+     * @param calSrc 减数
+     * @param calDes 被减数
+     * @return 两个日期之间的差值
+     */
+    public static int dateDiff(char flag, Calendar calSrc, Calendar calDes) {
+
+        long millisDiff = getMillis(calSrc) - getMillis(calDes);
+        char year = 'y';
+        char day = 'd';
+        char hour = 'h';
+        char minute = 'm';
+        char second = 's';
+
+        if (flag == year) {
+            return (calSrc.get(Calendar.YEAR) - calDes.get(Calendar.YEAR));
+        }
+
+        if (flag == day) {
+            return (int) (millisDiff / DAY_IN_MILLIS);
+        }
+
+        if (flag == hour) {
+            return (int) (millisDiff / HOUR_IN_MILLIS);
+        }
+
+        if (flag == minute) {
+            return (int) (millisDiff / MINUTE_IN_MILLIS);
+        }
+
+        if (flag == second) {
+            return (int) (millisDiff / SECOND_IN_MILLIS);
+        }
+
+        return 0;
+    }
+
+    public static Long getCurrentTimestamp() {
+        return Long.valueOf(DateUtils.yyyymmddhhmmss.get().format(new Date()));
+    }
+
+    /**
+     * String类型 转换为Date, 如果参数长度为10 转换格式”yyyy-MM-dd“ 如果参数长度为19 转换格式”yyyy-MM-dd
+     * HH:mm:ss“ * @param text String类型的时间值
+     */
+    @Override
+    public void setAsText(String text) throws IllegalArgumentException {
+        if (StringUtils.hasText(text)) {
+            try {
+                int length10 = 10;
+                int length19 = 19;
+                if (text.indexOf(SymbolConstant.COLON) == -1 && text.length() == length10) {
+                    setValue(DateUtils.date_sdf.get().parse(text));
+                } else if (text.indexOf(SymbolConstant.COLON) > 0 && text.length() == length19) {
+                    setValue(DateUtils.datetimeFormat.get().parse(text));
+                } else {
+                    throw new IllegalArgumentException("Could not parse date, date format is error ");
+                }
+            } catch (ParseException ex) {
+                IllegalArgumentException iae = new IllegalArgumentException("Could not parse date: " + ex.getMessage());
+                iae.initCause(ex);
+                throw iae;
+            }
+        } else {
+            setValue(null);
+        }
+    }
+
+    public static int getYear() {
+        GregorianCalendar calendar = new GregorianCalendar();
+        calendar.setTime(getDate());
+        return calendar.get(Calendar.YEAR);
+    }
+
+    /**
+     * 将字符串转成时间
+     * @param str
+     * @return
+     */
+    public static Date parseDatetime(String str){
+        try {
+            return datetimeFormat.get().parse(str);
+        }catch (Exception e){
+        }
+        return null;
+    }
+
+    /**
+     * 判断两个时间是否是同一天
+     *
+     * @param date1
+     * @param date2
+     * @return
+     */
+    public static boolean isSameDay(Date date1, Date date2) {
+        if (date1 == null || date2 == null) {
+            return false;
+        }
+        Calendar calendar1 = Calendar.getInstance();
+        calendar1.setTime(date1);
+        Calendar calendar2 = Calendar.getInstance();
+        calendar2.setTime(date2);
+        boolean isSameYear = calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
+        boolean isSameMonth = isSameYear && calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH);
+        return isSameMonth && calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH);
+    }
+
+    /**
+     * 判断两个时间是否是同一周
+     *
+     * @param date1
+     * @param date2
+     * @return
+     */
+    public static boolean isSameWeek(Date date1, Date date2) {
+        if (date1 == null || date2 == null) {
+            return false;
+        }
+        Calendar calendar1 = Calendar.getInstance();
+        calendar1.setTime(date1);
+        Calendar calendar2 = Calendar.getInstance();
+        calendar2.setTime(date2);
+        boolean isSameYear = calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
+        return isSameYear && calendar1.get(Calendar.WEEK_OF_YEAR) == calendar2.get(Calendar.WEEK_OF_YEAR);
+    }
+
+    /**
+     * 判断两个时间是否是同一月
+     *
+     * @param date1
+     * @param date2
+     * @return
+     */
+    public static boolean isSameMonth(Date date1, Date date2) {
+        if (date1 == null || date2 == null) {
+            return false;
+        }
+        Calendar calendar1 = Calendar.getInstance();
+        calendar1.setTime(date1);
+        Calendar calendar2 = Calendar.getInstance();
+        calendar2.setTime(date2);
+        boolean isSameYear = calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
+        return isSameYear && calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH);
+    }
+
+    /**
+     * 判断两个时间是否是同一年
+     *
+     * @param date1
+     * @param date2
+     * @return
+     */
+    public static boolean isSameYear(Date date1, Date date2) {
+        if (date1 == null || date2 == null) {
+            return false;
+        }
+        Calendar calendar1 = Calendar.getInstance();
+        calendar1.setTime(date1);
+        Calendar calendar2 = Calendar.getInstance();
+        calendar2.setTime(date2);
+        return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
+    }
+
+}

+ 126 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DySmsHelper.java

@@ -0,0 +1,126 @@
+package org.jeecg.common.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
+import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import org.jeecg.common.constant.enums.DySmsEnum;
+import org.jeecg.config.StaticConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created on 17/6/7.
+ * 短信API产品的DEMO程序,工程中包含了一个SmsDemo类,直接通过
+ * 执行main函数即可体验短信产品API功能(只需要将AK替换成开通了云通信-短信产品功能的AK即可)
+ * 工程依赖了2个jar包(存放在工程的libs目录下)
+ * 1:aliyun-java-sdk-core.jar
+ * 2:aliyun-java-sdk-dysmsapi.jar
+ *
+ * 备注:Demo工程编码采用UTF-8
+ * 国际短信发送请勿参照此DEMO
+ * @author: jeecg-boot
+ */
+public class DySmsHelper {
+	
+	private final static Logger logger=LoggerFactory.getLogger(DySmsHelper.class);
+
+    /**产品名称:云通信短信API产品,开发者无需替换*/
+    static final String PRODUCT = "Dysmsapi";
+    /**产品域名,开发者无需替换*/
+    static final String DOMAIN = "dysmsapi.aliyuncs.com";
+
+    /**TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)*/
+    static  String accessKeyId;
+    static  String accessKeySecret;
+
+    public static void setAccessKeyId(String accessKeyId) {
+        DySmsHelper.accessKeyId = accessKeyId;
+    }
+
+    public static void setAccessKeySecret(String accessKeySecret) {
+        DySmsHelper.accessKeySecret = accessKeySecret;
+    }
+
+    public static String getAccessKeyId() {
+        return accessKeyId;
+    }
+
+    public static String getAccessKeySecret() {
+        return accessKeySecret;
+    }
+    
+    
+    public static boolean sendSms(String phone, JSONObject templateParamJson, DySmsEnum dySmsEnum) throws ClientException {
+    	//可自助调整超时时间
+        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
+        System.setProperty("sun.net.client.defaultReadTimeout", "10000");
+
+        //update-begin-author:taoyan date:20200811 for:配置类数据获取
+        StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
+        //logger.info("阿里大鱼短信秘钥 accessKeyId:" + staticConfig.getAccessKeyId());
+        //logger.info("阿里大鱼短信秘钥 accessKeySecret:"+ staticConfig.getAccessKeySecret());
+        setAccessKeyId(staticConfig.getAccessKeyId());
+        setAccessKeySecret(staticConfig.getAccessKeySecret());
+        //update-end-author:taoyan date:20200811 for:配置类数据获取
+        
+        //初始化acsClient,暂不支持region化
+        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
+        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN);
+        IAcsClient acsClient = new DefaultAcsClient(profile);
+        
+        //验证json参数
+        validateParam(templateParamJson,dySmsEnum);
+        
+        //组装请求对象-具体描述见控制台-文档部分内容
+        SendSmsRequest request = new SendSmsRequest();
+        //必填:待发送手机号
+        request.setPhoneNumbers(phone);
+        //必填:短信签名-可在短信控制台中找到
+        request.setSignName(dySmsEnum.getSignName());
+        //必填:短信模板-可在短信控制台中找到
+        request.setTemplateCode(dySmsEnum.getTemplateCode());
+        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
+        request.setTemplateParam(templateParamJson.toJSONString());
+        
+        //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
+        //request.setSmsUpExtendCode("90997");
+
+        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
+        //request.setOutId("yourOutId");
+
+        boolean result = false;
+
+        //hint 此处可能会抛出异常,注意catch
+        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
+        logger.info("短信接口返回的数据----------------");
+        logger.info("{Code:" + sendSmsResponse.getCode()+",Message:" + sendSmsResponse.getMessage()+",RequestId:"+ sendSmsResponse.getRequestId()+",BizId:"+sendSmsResponse.getBizId()+"}");
+        String ok = "OK";
+        if (ok.equals(sendSmsResponse.getCode())) {
+            result = true;
+        }
+        return result;
+        
+    }
+    
+    private static void validateParam(JSONObject templateParamJson,DySmsEnum dySmsEnum) {
+    	String keys = dySmsEnum.getKeys();
+    	String [] keyArr = keys.split(",");
+    	for(String item :keyArr) {
+    		if(!templateParamJson.containsKey(item)) {
+    			throw new RuntimeException("模板缺少参数:"+item);
+    		}
+    	}
+    }
+    
+
+//    public static void main(String[] args) throws ClientException, InterruptedException {
+//    	JSONObject obj = new JSONObject();
+//    	obj.put("code", "1234");
+//    	sendSms("13800138000", obj, DySmsEnum.FORGET_PASSWORD_TEMPLATE_CODE);
+//    }
+}

+ 57 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FillRuleUtil.java

@@ -0,0 +1,57 @@
+package org.jeecg.common.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.jeecg.common.handler.IFillRuleHandler;
+
+
+/**
+ * 规则值自动生成工具类
+ *
+ * @author qinfeng
+ * @举例: 自动生成订单号;自动生成当前日期
+ */
+@Slf4j
+public class FillRuleUtil {
+
+    /**
+     * @param ruleCode ruleCode
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static Object executeRule(String ruleCode, JSONObject formData) {
+        if (!StringUtils.isEmpty(ruleCode)) {
+            try {
+                // 获取 Service
+                ServiceImpl impl = (ServiceImpl) SpringContextUtils.getBean("sysFillRuleServiceImpl");
+                // 根据 ruleCode 查询出实体
+                QueryWrapper queryWrapper = new QueryWrapper();
+                queryWrapper.eq("rule_code", ruleCode);
+                JSONObject entity = JSON.parseObject(JSON.toJSONString(impl.getOne(queryWrapper)));
+                if (entity == null) {
+                    log.warn("填值规则:" + ruleCode + " 不存在");
+                    return null;
+                }
+                // 获取必要的参数
+                String ruleClass = entity.getString("ruleClass");
+                JSONObject params = entity.getJSONObject("ruleParams");
+                if (params == null) {
+                    params = new JSONObject();
+                }
+                if (formData == null) {
+                    formData = new JSONObject();
+                }
+                // 通过反射执行配置的类里的方法
+                IFillRuleHandler ruleHandler = (IFillRuleHandler) Class.forName(ruleClass).newInstance();
+                return ruleHandler.execute(params, formData);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+}

+ 97 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ImportExcelUtil.java

@@ -0,0 +1,97 @@
+package org.jeecg.common.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.service.IService;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.constant.CommonConstant;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * 导出返回信息
+ * @author: jeecg-boot
+ */
+@Slf4j
+public class ImportExcelUtil {
+
+    public static Result<?> imporReturnRes(int errorLines,int successLines,List<String> errorMessage) throws IOException {
+        if (errorLines == 0) {
+            return Result.ok("共" + successLines + "行数据全部导入成功!");
+        } else {
+            JSONObject result = new JSONObject(5);
+            int totalCount = successLines + errorLines;
+            result.put("totalCount", totalCount);
+            result.put("errorCount", errorLines);
+            result.put("successCount", successLines);
+            result.put("msg", "总上传行数:" + totalCount + ",已导入行数:" + successLines + ",错误行数:" + errorLines);
+            String fileUrl = PmsUtil.saveErrorTxtByList(errorMessage, "userImportExcelErrorLog");
+            int lastIndex = fileUrl.lastIndexOf(File.separator);
+            String fileName = fileUrl.substring(lastIndex + 1);
+            result.put("fileUrl", "/sys/common/static/" + fileUrl);
+            result.put("fileName", fileName);
+            Result res = Result.ok(result);
+            res.setCode(201);
+            res.setMessage("文件导入成功,但有错误。");
+            return res;
+        }
+    }
+
+    public static List<String> importDateSave(List<?> list, Class serviceClass, List<String> errorMessage, String errorFlag)  {
+        IService bean =(IService) SpringContextUtils.getBean(serviceClass);
+        for (int i = 0; i < list.size(); i++) {
+            try {
+                boolean save = bean.save(list.get(i));
+                if(!save){
+                    throw new Exception(errorFlag);
+                }
+            } catch (Exception e) {
+                String message = e.getMessage().toLowerCase();
+                int lineNumber = i + 1;
+                // 通过索引名判断出错信息
+                if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_ROLE_CODE)) {
+                    errorMessage.add("第 " + lineNumber + " 行:角色编码已经存在,忽略导入。");
+                } else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_JOB_CLASS_NAME)) {
+                    errorMessage.add("第 " + lineNumber + " 行:任务类名已经存在,忽略导入。");
+                }else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_CODE)) {
+                    errorMessage.add("第 " + lineNumber + " 行:职务编码已经存在,忽略导入。");
+                }else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_DEPART_ORG_CODE)) {
+                    errorMessage.add("第 " + lineNumber + " 行:部门编码已经存在,忽略导入。");
+                }else {
+                    errorMessage.add("第 " + lineNumber + " 行:未知错误,忽略导入");
+                    log.error(e.getMessage(), e);
+                }
+            }
+        }
+        return errorMessage;
+    }
+
+    public static List<String> importDateSaveOne(Object obj, Class serviceClass,List<String> errorMessage,int i,String errorFlag)  {
+        IService bean =(IService) SpringContextUtils.getBean(serviceClass);
+        try {
+            boolean save = bean.save(obj);
+            if(!save){
+                throw new Exception(errorFlag);
+            }
+        } catch (Exception e) {
+            String message = e.getMessage().toLowerCase();
+            int lineNumber = i + 1;
+            // 通过索引名判断出错信息
+            if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_ROLE_CODE)) {
+                errorMessage.add("第 " + lineNumber + " 行:角色编码已经存在,忽略导入。");
+            } else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_JOB_CLASS_NAME)) {
+                errorMessage.add("第 " + lineNumber + " 行:任务类名已经存在,忽略导入。");
+            }else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_CODE)) {
+                errorMessage.add("第 " + lineNumber + " 行:职务编码已经存在,忽略导入。");
+            }else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_DEPART_ORG_CODE)) {
+                errorMessage.add("第 " + lineNumber + " 行:部门编码已经存在,忽略导入。");
+            }else {
+                errorMessage.add("第 " + lineNumber + " 行:未知错误,忽略导入");
+                log.error(e.getMessage(), e);
+            }
+        }
+        return errorMessage;
+    }
+}

+ 59 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/IpUtils.java

@@ -0,0 +1,59 @@
+package org.jeecg.common.util;
+
+import org.apache.commons.lang.StringUtils;
+import org.jeecg.common.constant.CommonConstant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * IP地址
+ * 
+ * @Author scott
+ * @email jeecgos@163.com
+ * @Date 2019年01月14日
+ */
+public class IpUtils {
+	private static Logger logger = LoggerFactory.getLogger(IpUtils.class);
+
+	/**
+	 * 获取IP地址
+	 * 
+	 * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
+	 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
+	 */
+	public static String getIpAddr(HttpServletRequest request) {
+    	String ip = null;
+        try {
+            ip = request.getHeader("x-forwarded-for");
+            if (StringUtils.isEmpty(ip) || CommonConstant.UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("Proxy-Client-IP");
+            }
+            if (StringUtils.isEmpty(ip) || ip.length() == 0 ||CommonConstant.UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("WL-Proxy-Client-IP");
+            }
+            if (StringUtils.isEmpty(ip) || CommonConstant.UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_CLIENT_IP");
+            }
+            if (StringUtils.isEmpty(ip) || CommonConstant.UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+            }
+            if (StringUtils.isEmpty(ip) || CommonConstant.UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getRemoteAddr();
+            }
+        } catch (Exception e) {
+        	logger.error("IPUtils ERROR ", e);
+        }
+        
+//        //使用代理,则获取第一个IP地址
+//        if(StringUtils.isEmpty(ip) && ip.length() > 15) {
+//			if(ip.indexOf(",") > 0) {
+//				ip = ip.substring(0, ip.indexOf(","));
+//			}
+//		}
+        
+        return ip;
+    }
+	
+}

+ 47 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Md5Util.java

@@ -0,0 +1,47 @@
+package org.jeecg.common.util;
+
+import java.security.MessageDigest;
+
+/**
+ * @Description: 加密工具
+ * @author: jeecg-boot
+ */
+public class Md5Util {
+
+    private static final String[] HEXDIGITS = { "0", "1", "2", "3", "4", "5",
+            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
+
+	public static String byteArrayToHexString(byte[] b) {
+		StringBuffer resultSb = new StringBuffer();
+		for (int i = 0; i < b.length; i++){
+			resultSb.append(byteToHexString(b[i]));
+		}
+		return resultSb.toString();
+	}
+
+	private static String byteToHexString(byte b) {
+		int n = b;
+		if (n < 0) {
+			n += 256;
+		}
+		int d1 = n / 16;
+		int d2 = n % 16;
+		return HEXDIGITS[d1] + HEXDIGITS[d2];
+	}
+
+	public static String md5Encode(String origin, String charsetname) {
+		String resultString = null;
+		try {
+			resultString = new String(origin);
+			MessageDigest md = MessageDigest.getInstance("MD5");
+			if (charsetname == null || "".equals(charsetname)) {
+				resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
+			} else {
+				resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
+			}
+		} catch (Exception exception) {
+		}
+		return resultString;
+	}
+
+}

+ 219 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MinioUtil.java

@@ -0,0 +1,219 @@
+package org.jeecg.common.util;
+
+import io.minio.*;
+import io.minio.http.Method;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.constant.SymbolConstant;
+import org.jeecg.common.util.filter.StrAttackFilter;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.InputStream;
+import java.net.URLDecoder;
+
+/**
+ * minio文件上传工具类
+ * @author: jeecg-boot
+ */
+@Slf4j
+public class MinioUtil {
+    private static String minioUrl;
+    private static String minioName;
+    private static String minioPass;
+    private static String bucketName;
+
+    public static void setMinioUrl(String minioUrl) {
+        MinioUtil.minioUrl = minioUrl;
+    }
+
+    public static void setMinioName(String minioName) {
+        MinioUtil.minioName = minioName;
+    }
+
+    public static void setMinioPass(String minioPass) {
+        MinioUtil.minioPass = minioPass;
+    }
+
+    public static void setBucketName(String bucketName) {
+        MinioUtil.bucketName = bucketName;
+    }
+
+    public static String getMinioUrl() {
+        return minioUrl;
+    }
+
+    public static String getBucketName() {
+        return bucketName;
+    }
+
+    private static MinioClient minioClient = null;
+
+    /**
+     * 上传文件
+     * @param file
+     * @return
+     */
+    public static String upload(MultipartFile file, String bizPath, String customBucket) throws Exception {
+        String fileUrl = "";
+        //update-begin-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
+        bizPath = StrAttackFilter.filter(bizPath);
+        //update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
+
+        String newBucket = bucketName;
+        if(oConvertUtils.isNotEmpty(customBucket)){
+            newBucket = customBucket;
+        }
+        try {
+            initMinio(minioUrl, minioName,minioPass);
+            // 检查存储桶是否已经存在
+            if(minioClient.bucketExists(BucketExistsArgs.builder().bucket(newBucket).build())) {
+                log.info("Bucket already exists.");
+            } else {
+                // 创建一个名为ota的存储桶
+                minioClient.makeBucket(MakeBucketArgs.builder().bucket(newBucket).build());
+                log.info("create a new bucket.");
+            }
+            InputStream stream = file.getInputStream();
+            // 获取文件名
+            String orgName = file.getOriginalFilename();
+            if("".equals(orgName)){
+                orgName=file.getName();
+            }
+            orgName = CommonUtils.getFileName(orgName);
+            String objectName = bizPath+"/"
+                                +( orgName.indexOf(".")==-1
+                                   ?orgName + "_" + System.currentTimeMillis()
+                                   :orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."))
+                                 );
+
+            // 使用putObject上传一个本地文件到存储桶中。
+            if(objectName.startsWith(SymbolConstant.SINGLE_SLASH)){
+                objectName = objectName.substring(1);
+            }
+            PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName)
+                    .bucket(newBucket)
+                    .contentType("application/octet-stream")
+                    .stream(stream,stream.available(),-1).build();
+            minioClient.putObject(objectArgs);
+            stream.close();
+            fileUrl = minioUrl+newBucket+"/"+objectName;
+        }catch (Exception e){
+            log.error(e.getMessage(), e);
+        }
+        return fileUrl;
+    }
+
+    /**
+     * 文件上传
+     * @param file
+     * @param bizPath
+     * @return
+     */
+    public static String upload(MultipartFile file, String bizPath) throws Exception {
+        return upload(file,bizPath,null);
+    }
+
+    /**
+     * 获取文件流
+     * @param bucketName
+     * @param objectName
+     * @return
+     */
+    public static InputStream getMinioFile(String bucketName,String objectName){
+        InputStream inputStream = null;
+        try {
+            initMinio(minioUrl, minioName, minioPass);
+            GetObjectArgs objectArgs = GetObjectArgs.builder().object(objectName)
+                    .bucket(bucketName).build();
+            inputStream = minioClient.getObject(objectArgs);
+        } catch (Exception e) {
+            log.info("文件获取失败" + e.getMessage());
+        }
+        return inputStream;
+    }
+
+    /**
+     * 删除文件
+     * @param bucketName
+     * @param objectName
+     * @throws Exception
+     */
+    public static void removeObject(String bucketName, String objectName) {
+        try {
+            initMinio(minioUrl, minioName,minioPass);
+            RemoveObjectArgs objectArgs = RemoveObjectArgs.builder().object(objectName)
+                    .bucket(bucketName).build();
+            minioClient.removeObject(objectArgs);
+        }catch (Exception e){
+            log.info("文件删除失败" + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取文件外链
+     * @param bucketName
+     * @param objectName
+     * @param expires
+     * @return
+     */
+    public static String getObjectUrl(String bucketName, String objectName, Integer expires) {
+        initMinio(minioUrl, minioName,minioPass);
+        try{
+            //update-begin---author:liusq  Date:20220121  for:获取文件外链报错提示method不能为空,导致文件下载和预览失败----
+            GetPresignedObjectUrlArgs objectArgs = GetPresignedObjectUrlArgs.builder().object(objectName)
+                    .bucket(bucketName)
+                    .expiry(expires).method(Method.GET).build();
+            //update-begin---author:liusq  Date:20220121  for:获取文件外链报错提示method不能为空,导致文件下载和预览失败----
+            String url = minioClient.getPresignedObjectUrl(objectArgs);
+            return URLDecoder.decode(url,"UTF-8");
+        }catch (Exception e){
+            log.info("文件路径获取失败" + e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * 初始化客户端
+     * @param minioUrl
+     * @param minioName
+     * @param minioPass
+     * @return
+     */
+    private static MinioClient initMinio(String minioUrl, String minioName,String minioPass) {
+        if (minioClient == null) {
+            try {
+                minioClient = MinioClient.builder()
+                        .endpoint(minioUrl)
+                        .credentials(minioName, minioPass)
+                        .build();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return minioClient;
+    }
+
+    /**
+     * 上传文件到minio
+     * @param stream
+     * @param relativePath
+     * @return
+     */
+    public static String upload(InputStream stream,String relativePath) throws Exception {
+        initMinio(minioUrl, minioName,minioPass);
+        if(minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
+            log.info("Bucket already exists.");
+        } else {
+            // 创建一个名为ota的存储桶
+            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
+            log.info("create a new bucket.");
+        }
+        PutObjectArgs objectArgs = PutObjectArgs.builder().object(relativePath)
+                .bucket(bucketName)
+                .contentType("application/octet-stream")
+                .stream(stream,stream.available(),-1).build();
+        minioClient.putObject(objectArgs);
+        stream.close();
+        return minioUrl+bucketName+"/"+relativePath;
+    }
+
+}

+ 189 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PasswordUtil.java

@@ -0,0 +1,189 @@
+package org.jeecg.common.util;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import java.security.Key;
+import java.security.SecureRandom;
+
+/**
+ * @Description: 密码工具类
+ * @author: jeecg-boot
+ */
+public class PasswordUtil {
+
+	/**
+	 * JAVA6支持以下任意一种算法 PBEWITHMD5ANDDES PBEWITHMD5ANDTRIPLEDES
+	 * PBEWITHSHAANDDESEDE PBEWITHSHA1ANDRC2_40 PBKDF2WITHHMACSHA1
+	 * */
+
+    /**
+     * 定义使用的算法为:PBEWITHMD5andDES算法
+     * 加密算法
+     */
+	public static final String ALGORITHM = "PBEWithMD5AndDES";
+
+    /**
+     * 定义使用的算法为:PBEWITHMD5andDES算法
+     * 密钥
+     */
+	public static final String SALT = "63293188";
+
+	/**
+	 * 定义迭代次数为1000次
+	 */
+	private static final int ITERATIONCOUNT = 1000;
+
+	/**
+	 * 获取加密算法中使用的盐值,解密中使用的盐值必须与加密中使用的相同才能完成操作. 盐长度必须为8字节
+	 * 
+	 * @return byte[] 盐值
+	 * */
+	public static byte[] getSalt() throws Exception {
+		// 实例化安全随机数
+		SecureRandom random = new SecureRandom();
+		// 产出盐
+		return random.generateSeed(8);
+	}
+
+	public static byte[] getStaticSalt() {
+		// 产出盐
+		return SALT.getBytes();
+	}
+
+	/**
+	 * 根据PBE密码生成一把密钥
+	 * 
+	 * @param password
+	 *            生成密钥时所使用的密码
+	 * @return Key PBE算法密钥
+	 * */
+	private static Key getPbeKey(String password) {
+		// 实例化使用的算法
+		SecretKeyFactory keyFactory;
+		SecretKey secretKey = null;
+		try {
+			keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
+			// 设置PBE密钥参数
+			PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
+			// 生成密钥
+			secretKey = keyFactory.generateSecret(keySpec);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+		return secretKey;
+	}
+
+	/**
+	 * 加密明文字符串
+	 * 
+	 * @param plaintext
+	 *            待加密的明文字符串
+	 * @param password
+	 *            生成密钥时所使用的密码
+	 * @param salt
+	 *            盐值
+	 * @return 加密后的密文字符串
+	 * @throws Exception
+	 */
+	public static String encrypt(String plaintext, String password, String salt) {
+
+		Key key = getPbeKey(password);
+		byte[] encipheredData = null;
+		PBEParameterSpec parameterSpec = new PBEParameterSpec(salt.getBytes(), ITERATIONCOUNT);
+		try {
+			Cipher cipher = Cipher.getInstance(ALGORITHM);
+
+			cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
+			//update-begin-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
+			encipheredData = cipher.doFinal(plaintext.getBytes("utf-8"));
+			//update-end-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
+		} catch (Exception e) {
+		}
+		return bytesToHexString(encipheredData);
+	}
+
+	/**
+	 * 解密密文字符串
+	 * 
+	 * @param ciphertext
+	 *            待解密的密文字符串
+	 * @param password
+	 *            生成密钥时所使用的密码(如需解密,该参数需要与加密时使用的一致)
+	 * @param salt
+	 *            盐值(如需解密,该参数需要与加密时使用的一致)
+	 * @return 解密后的明文字符串
+	 * @throws Exception
+	 */
+	public static String decrypt(String ciphertext, String password, String salt) {
+
+		Key key = getPbeKey(password);
+		byte[] passDec = null;
+		PBEParameterSpec parameterSpec = new PBEParameterSpec(salt.getBytes(), ITERATIONCOUNT);
+		try {
+			Cipher cipher = Cipher.getInstance(ALGORITHM);
+
+			cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
+
+			passDec = cipher.doFinal(hexStringToBytes(ciphertext));
+		}
+
+		catch (Exception e) {
+		}
+		return new String(passDec);
+	}
+
+	/**
+	 * 将字节数组转换为十六进制字符串
+	 * 
+	 * @param src
+	 *            字节数组
+	 * @return
+	 */
+	public static String bytesToHexString(byte[] src) {
+		StringBuilder stringBuilder = new StringBuilder("");
+		if (src == null || src.length <= 0) {
+			return null;
+		}
+		for (int i = 0; i < src.length; i++) {
+			int v = src[i] & 0xFF;
+			String hv = Integer.toHexString(v);
+			if (hv.length() < 2) {
+				stringBuilder.append(0);
+			}
+			stringBuilder.append(hv);
+		}
+		return stringBuilder.toString();
+	}
+
+	/**
+	 * 将十六进制字符串转换为字节数组
+	 * 
+	 * @param hexString
+	 *            十六进制字符串
+	 * @return
+	 */
+	public static byte[] hexStringToBytes(String hexString) {
+		if (hexString == null || "".equals(hexString)) {
+			return null;
+		}
+		hexString = hexString.toUpperCase();
+		int length = hexString.length() / 2;
+		char[] hexChars = hexString.toCharArray();
+		byte[] d = new byte[length];
+		for (int i = 0; i < length; i++) {
+			int pos = i * 2;
+			d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+		}
+		return d;
+	}
+
+	private static byte charToByte(char c) {
+		return (byte) "0123456789ABCDEF".indexOf(c);
+	}
+
+
+}

+ 65 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PmsUtil.java

@@ -0,0 +1,65 @@
+package org.jeecg.common.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @Description: PmsUtil
+ * @author: jeecg-boot
+ */
+@Slf4j
+@Component
+public class PmsUtil {
+
+
+    private static String uploadPath;
+
+    @Value("${jeecg.path.upload:}")
+    public void setUploadPath(String uploadPath) {
+        PmsUtil.uploadPath = uploadPath;
+    }
+
+    public static String saveErrorTxtByList(List<String> msg, String name) {
+        Date d = new Date();
+        String saveDir = "logs" + File.separator + DateUtils.yyyyMMdd.get().format(d) + File.separator;
+        String saveFullDir = uploadPath + File.separator + saveDir;
+
+        File saveFile = new File(saveFullDir);
+        if (!saveFile.exists()) {
+            saveFile.mkdirs();
+        }
+        name += DateUtils.yyyymmddhhmmss.get().format(d) + Math.round(Math.random() * 10000);
+        String saveFilePath = saveFullDir + name + ".txt";
+
+        try {
+            //封装目的地
+            BufferedWriter bw = new BufferedWriter(new FileWriter(saveFilePath));
+            //遍历集合
+            for (String s : msg) {
+                //写数据
+                if (s.indexOf("_") > 0) {
+                    String[] arr = s.split("_");
+                    bw.write("第" + arr[0] + "行:" + arr[1]);
+                } else {
+                    bw.write(s);
+                }
+                //bw.newLine();
+                bw.write("\r\n");
+            }
+            //释放资源
+            bw.flush();
+            bw.close();
+        } catch (Exception e) {
+            log.info("excel导入生成错误日志文件异常:" + e.getMessage());
+        }
+        return saveDir + name + ".txt";
+    }
+
+}

+ 339 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ReflectHelper.java

@@ -0,0 +1,339 @@
+package org.jeecg.common.util;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.extern.slf4j.Slf4j;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author 张代浩
+ * @desc 通过反射来动态调用get 和 set 方法
+ */
+@Slf4j
+public class ReflectHelper {
+
+    private Class cls;
+
+    /**
+     * 传过来的对象
+     */
+    private Object obj;
+
+    /**
+     * 存放get方法
+     */
+    private Hashtable<String, Method> getMethods = null;
+    /**
+     * 存放set方法
+     */
+    private Hashtable<String, Method> setMethods = null;
+
+    /**
+     * 定义构造方法 -- 一般来说是个pojo
+     *
+     * @param o 目标对象
+     */
+    public ReflectHelper(Object o) {
+        obj = o;
+        initMethods();
+    }
+
+    /**
+     * @desc 初始化
+     */
+    public void initMethods() {
+        getMethods = new Hashtable<String, Method>();
+        setMethods = new Hashtable<String, Method>();
+        cls = obj.getClass();
+        Method[] methods = cls.getMethods();
+        // 定义正则表达式,从方法中过滤出getter / setter 函数.
+        String gs = "get(\\w+)";
+        Pattern getM = Pattern.compile(gs);
+        String ss = "set(\\w+)";
+        Pattern setM = Pattern.compile(ss);
+        // 把方法中的"set" 或者 "get" 去掉
+        String rapl = "$1";
+        String param;
+        for (int i = 0; i < methods.length; ++i) {
+            Method m = methods[i];
+            String methodName = m.getName();
+            if (Pattern.matches(gs, methodName)) {
+                param = getM.matcher(methodName).replaceAll(rapl).toLowerCase();
+                getMethods.put(param, m);
+            } else if (Pattern.matches(ss, methodName)) {
+                param = setM.matcher(methodName).replaceAll(rapl).toLowerCase();
+                setMethods.put(param, m);
+            } else {
+                // logger.info(methodName + " 不是getter,setter方法!");
+            }
+        }
+    }
+
+    /**
+     * @desc 调用set方法
+     */
+    public boolean setMethodValue(String property, Object object) {
+        Method m = setMethods.get(property.toLowerCase());
+        if (m != null) {
+            try {
+                // 调用目标类的setter函数
+                m.invoke(obj, object);
+                return true;
+            } catch (Exception ex) {
+                log.info("invoke getter on " + property + " error: " + ex.toString());
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @desc 调用set方法
+     */
+    public Object getMethodValue(String property) {
+        Object value = null;
+        Method m = getMethods.get(property.toLowerCase());
+        if (m != null) {
+            try {
+                /*
+                 * 调用obj类的setter函数
+                 */
+                value = m.invoke(obj, new Object[]{});
+
+            } catch (Exception ex) {
+                log.info("invoke getter on " + property + " error: " + ex.toString());
+            }
+        }
+        return value;
+    }
+
+    /**
+     * 把map中的内容全部注入到obj中
+     *
+     * @param data
+     * @return
+     */
+    public Object setAll(Map<String, Object> data) {
+        if (data == null || data.keySet().size() <= 0) {
+            return null;
+        }
+        for (Entry<String, Object> entry : data.entrySet()) {
+            this.setMethodValue(entry.getKey(), entry.getValue());
+        }
+        return obj;
+    }
+
+    /**
+     * 把map中的内容全部注入到obj中
+     *
+     * @param o
+     * @param data
+     * @return
+     */
+    public static Object setAll(Object o, Map<String, Object> data) {
+        ReflectHelper reflectHelper = new ReflectHelper(o);
+        reflectHelper.setAll(data);
+        return o;
+    }
+
+    /**
+     * 把map中的内容全部注入到新实例中
+     *
+     * @param clazz
+     * @param data
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T setAll(Class<T> clazz, Map<String, Object> data) {
+        T o = null;
+        try {
+            o = clazz.newInstance();
+        } catch (Exception e) {
+            e.printStackTrace();
+            o = null;
+            return o;
+        }
+        return (T) setAll(o, data);
+    }
+
+    /**
+     * 根据传入的class将mapList转换为实体类list
+     *
+     * @param mapist
+     * @param clazz
+     * @return
+     */
+    public static <T> List<T> transList2Entrys(List<Map<String, Object>> mapist, Class<T> clazz) {
+        List<T> list = new ArrayList<T>();
+        if (mapist != null && mapist.size() > 0) {
+            for (Map<String, Object> data : mapist) {
+                list.add(ReflectHelper.setAll(clazz, data));
+            }
+        }
+        return list;
+    }
+
+    /**
+     * 根据属性名获取属性值
+     */
+    public static Object getFieldValueByName(String fieldName, Object o) {
+        try {
+            String firstLetter = fieldName.substring(0, 1).toUpperCase();
+            String getter = "get" + firstLetter + fieldName.substring(1);
+            Method method = o.getClass().getMethod(getter, new Class[]{});
+            Object value = method.invoke(o, new Object[]{});
+            return value;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 获取属性值
+     */
+    public static Object getFieldVal(String fieldName, Object o) {
+        try {
+            // 暴力反射获取属性
+            Field filed = o.getClass().getDeclaredField(fieldName);
+            // 设置反射时取消Java的访问检查,暴力访问
+            filed.setAccessible(true);
+            Object val = filed.get(o);
+            return val;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 获取属性名数组
+     */
+    public static String[] getFiledName(Object o) {
+        Field[] fields = o.getClass().getDeclaredFields();
+        String[] fieldNames = new String[fields.length];
+        for (int i = 0; i < fields.length; i++) {
+            //log.info(fields[i].getType());
+            fieldNames[i] = fields[i].getName();
+        }
+        return fieldNames;
+    }
+
+    /**
+     * 获取属性类型(type),属性名(name),属性值(value)的map组成的list
+     */
+    public static List<Map> getFiledsInfo(Object o) {
+        Field[] fields = o.getClass().getDeclaredFields();
+        String[] fieldNames = new String[fields.length];
+        List<Map> list = new ArrayList<Map>();
+        Map<String, Object> infoMap = null;
+        for (int i = 0; i < fields.length; i++) {
+            infoMap = new HashMap<>(5);
+            infoMap.put("type", fields[i].getType().toString());
+            infoMap.put("name", fields[i].getName());
+            infoMap.put("value", getFieldValueByName(fields[i].getName(), o));
+            list.add(infoMap);
+        }
+        return list;
+    }
+
+    /**
+     * 获取对象的所有属性值,返回一个对象数组
+     */
+    public static Object[] getFiledValues(Object o) {
+        String[] fieldNames = getFiledName(o);
+        Object[] value = new Object[fieldNames.length];
+        for (int i = 0; i < fieldNames.length; i++) {
+            value[i] = getFieldValueByName(fieldNames[i], o);
+        }
+        return value;
+    }
+
+    /**
+     * 判断给定的字段是不是类中的属性
+     * @param field 字段名
+     * @param clazz 类对象
+     * @return
+     */
+    public static boolean isClassField(String field, Class clazz){
+        Field[] fields = clazz.getDeclaredFields();
+        for(int i=0;i<fields.length;i++){
+            String fieldName = fields[i].getName();
+            String tableColumnName = oConvertUtils.camelToUnderline(fieldName);
+            if(fieldName.equalsIgnoreCase(field) || tableColumnName.equalsIgnoreCase(field)){
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 获取class的 包括父类的
+     * @param clazz
+     * @return
+     */
+    public static List<Field> getClassFields(Class<?> clazz) {
+        List<Field> list = new ArrayList<Field>();
+        Field[] fields;
+        do{
+            fields = clazz.getDeclaredFields();
+            for(int i = 0;i<fields.length;i++){
+                list.add(fields[i]);
+            }
+            clazz = clazz.getSuperclass();
+        }while(clazz!= Object.class&&clazz!=null);
+        return list;
+    }
+
+    /**
+     * 获取表字段名
+     * @param clazz
+     * @param name
+     * @return
+     */
+    public static String getTableFieldName(Class<?> clazz, String name) {
+        try {
+            //如果字段加注解了@TableField(exist = false),不走DB查询
+            Field field = null;
+            try {
+                field = clazz.getDeclaredField(name);
+            } catch (NoSuchFieldException e) {
+                //e.printStackTrace();
+            }
+
+            //如果为空,则去父类查找字段
+            if (field == null) {
+                List<Field> allFields = getClassFields(clazz);
+                List<Field> searchFields = allFields.stream().filter(a -> a.getName().equals(name)).collect(Collectors.toList());
+                if(searchFields!=null && searchFields.size()>0){
+                    field = searchFields.get(0);
+                }
+            }
+
+            if (field != null) {
+                TableField tableField = field.getAnnotation(TableField.class);
+                if (tableField != null){
+                    if(tableField.exist() == false){
+                        //如果设置了TableField false 这个字段不需要处理
+                        return null;
+                    }else{
+                        String column = tableField.value();
+                        //如果设置了TableField value 这个字段是实体字段
+                        if(!"".equals(column)){
+                            return column;
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return name;
+    }
+
+}

+ 263 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RestUtil.java

@@ -0,0 +1,263 @@
+package org.jeecg.common.util;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.http.*;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * 调用 Restful 接口 Util
+ *
+ * @author sunjianlei
+ */
+@Slf4j
+public class RestUtil {
+
+    private static String domain = null;
+    private static String path = null;
+    
+    private static String getDomain() {
+        if (domain == null) {
+            domain = SpringContextUtils.getDomain();
+            // issues/2959
+            // 微服务版集成企业微信单点登录
+            // 因为微服务版没有端口号,导致 SpringContextUtils.getDomain() 方法获取的域名的端口号变成了:-1所以出问题了,只需要把这个-1给去掉就可以了。
+            String port=":-1";
+            if (domain.endsWith(port)) {
+                domain = domain.substring(0, domain.length() - 3);
+            }
+        }
+        return domain;
+    }
+
+    private static String getPath() {
+        if (path == null) {
+            path = SpringContextUtils.getApplicationContext().getEnvironment().getProperty("server.servlet.context-path");
+        }
+        return oConvertUtils.getString(path);
+    }
+
+    public static String getBaseUrl() {
+        String basepath = getDomain() + getPath();
+        log.info(" RestUtil.getBaseUrl: " + basepath);
+        return basepath;
+    }
+
+    /**
+     * RestAPI 调用器
+     */
+    private final static RestTemplate RT;
+
+    static {
+        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
+        requestFactory.setConnectTimeout(3000);
+        requestFactory.setReadTimeout(3000);
+        RT = new RestTemplate(requestFactory);
+        // 解决乱码问题
+        RT.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
+    }
+
+    public static RestTemplate getRestTemplate() {
+        return RT;
+    }
+
+    /**
+     * 发送 get 请求
+     */
+    public static JSONObject get(String url) {
+        return getNative(url, null, null).getBody();
+    }
+
+    /**
+     * 发送 get 请求
+     */
+    public static JSONObject get(String url, JSONObject variables) {
+        return getNative(url, variables, null).getBody();
+    }
+
+    /**
+     * 发送 get 请求
+     */
+    public static JSONObject get(String url, JSONObject variables, JSONObject params) {
+        return getNative(url, variables, params).getBody();
+    }
+
+    /**
+     * 发送 get 请求,返回原生 ResponseEntity 对象
+     */
+    public static ResponseEntity<JSONObject> getNative(String url, JSONObject variables, JSONObject params) {
+        return request(url, HttpMethod.GET, variables, params);
+    }
+
+    /**
+     * 发送 Post 请求
+     */
+    public static JSONObject post(String url) {
+        return postNative(url, null, null).getBody();
+    }
+
+    /**
+     * 发送 Post 请求
+     */
+    public static JSONObject post(String url, JSONObject params) {
+        return postNative(url, null, params).getBody();
+    }
+
+    /**
+     * 发送 Post 请求
+     */
+    public static JSONObject post(String url, JSONObject variables, JSONObject params) {
+        return postNative(url, variables, params).getBody();
+    }
+
+    /**
+     * 发送 POST 请求,返回原生 ResponseEntity 对象
+     */
+    public static ResponseEntity<JSONObject> postNative(String url, JSONObject variables, JSONObject params) {
+        return request(url, HttpMethod.POST, variables, params);
+    }
+
+    /**
+     * 发送 put 请求
+     */
+    public static JSONObject put(String url) {
+        return putNative(url, null, null).getBody();
+    }
+
+    /**
+     * 发送 put 请求
+     */
+    public static JSONObject put(String url, JSONObject params) {
+        return putNative(url, null, params).getBody();
+    }
+
+    /**
+     * 发送 put 请求
+     */
+    public static JSONObject put(String url, JSONObject variables, JSONObject params) {
+        return putNative(url, variables, params).getBody();
+    }
+
+    /**
+     * 发送 put 请求,返回原生 ResponseEntity 对象
+     */
+    public static ResponseEntity<JSONObject> putNative(String url, JSONObject variables, JSONObject params) {
+        return request(url, HttpMethod.PUT, variables, params);
+    }
+
+    /**
+     * 发送 delete 请求
+     */
+    public static JSONObject delete(String url) {
+        return deleteNative(url, null, null).getBody();
+    }
+
+    /**
+     * 发送 delete 请求
+     */
+    public static JSONObject delete(String url, JSONObject variables, JSONObject params) {
+        return deleteNative(url, variables, params).getBody();
+    }
+
+    /**
+     * 发送 delete 请求,返回原生 ResponseEntity 对象
+     */
+    public static ResponseEntity<JSONObject> deleteNative(String url, JSONObject variables, JSONObject params) {
+        return request(url, HttpMethod.DELETE, null, variables, params, JSONObject.class);
+    }
+
+    /**
+     * 发送请求
+     */
+    public static ResponseEntity<JSONObject> request(String url, HttpMethod method, JSONObject variables, JSONObject params) {
+        return request(url, method, getHeaderApplicationJson(), variables, params, JSONObject.class);
+    }
+
+    /**
+     * 发送请求
+     *
+     * @param url          请求地址
+     * @param method       请求方式
+     * @param headers      请求头  可空
+     * @param variables    请求url参数 可空
+     * @param params       请求body参数 可空
+     * @param responseType 返回类型
+     * @return ResponseEntity<responseType>
+     */
+    public static <T> ResponseEntity<T> request(String url, HttpMethod method, HttpHeaders headers, JSONObject variables, Object params, Class<T> responseType) {
+        log.info(" RestUtil  --- request ---  url = "+ url);
+        if (StringUtils.isEmpty(url)) {
+            throw new RuntimeException("url 不能为空");
+        }
+        if (method == null) {
+            throw new RuntimeException("method 不能为空");
+        }
+        if (headers == null) {
+            headers = new HttpHeaders();
+        }
+        // 请求体
+        String body = "";
+        if (params != null) {
+            if (params instanceof JSONObject) {
+                body = ((JSONObject) params).toJSONString();
+
+            } else {
+                body = params.toString();
+            }
+        }
+        // 拼接 url 参数
+        if (variables != null && !variables.isEmpty()) {
+            url += ("?" + asUrlVariables(variables));
+        }
+        // 发送请求
+        HttpEntity<String> request = new HttpEntity<>(body, headers);
+        return RT.exchange(url, method, request, responseType);
+    }
+
+    /**
+     * 获取JSON请求头
+     */
+    public static HttpHeaders getHeaderApplicationJson() {
+        return getHeader(MediaType.APPLICATION_JSON_UTF8_VALUE);
+    }
+
+    /**
+     * 获取请求头
+     */
+    public static HttpHeaders getHeader(String mediaType) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.parseMediaType(mediaType));
+        headers.add("Accept", mediaType);
+        return headers;
+    }
+
+    /**
+     * 将 JSONObject 转为 a=1&b=2&c=3...&n=n 的形式
+     */
+    public static String asUrlVariables(JSONObject variables) {
+        Map<String, Object> source = variables.getInnerMap();
+        Iterator<String> it = source.keySet().iterator();
+        StringBuilder urlVariables = new StringBuilder();
+        while (it.hasNext()) {
+            String key = it.next();
+            String value = "";
+            Object object = source.get(key);
+            if (object != null) {
+                if (!StringUtils.isEmpty(object.toString())) {
+                    value = object.toString();
+                }
+            }
+            urlVariables.append("&").append(key).append("=").append(value);
+        }
+        // 去掉第一个&
+        return urlVariables.substring(1);
+    }
+
+}

+ 69 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ScheduledThreadPoolUtil.java

@@ -0,0 +1,69 @@
+package org.jeecg.common.util;
+
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 线程池工具类
+ *
+ * @author sunweidong
+ * @since 2024/6/4
+ */
+public class ScheduledThreadPoolUtil {
+
+    /**
+     * 核心线程数
+     */
+    private static final int CORE_SIZE = 2;
+    /**
+     * 工厂名
+     */
+    private static final String THREAD_FACTORY_NAME = "SERVICE-SCHEDULED-THREAD-POOL";
+
+    /**
+     * 私有化构造
+     */
+    private ScheduledThreadPoolUtil() {
+    }
+
+    private static class Inner {
+        private static final ScheduledThreadPoolExecutor THREAD_POOL_EXECUTOR =
+                new ScheduledThreadPoolExecutor(CORE_SIZE, defaultThreadFactory(), defaultRejectHandler());
+    }
+
+    public static ScheduledThreadPoolExecutor getInstance() {
+        return Inner.THREAD_POOL_EXECUTOR;
+    }
+
+    private static CustomThreadFactory defaultThreadFactory() {
+        return new CustomThreadFactory(THREAD_FACTORY_NAME);
+    }
+
+    private static CustomRejectHandler defaultRejectHandler() {
+        return new CustomRejectHandler();
+    }
+
+    static class CustomThreadFactory implements ThreadFactory {
+        private final String PREFIX_NAME;
+        private final AtomicInteger id = new AtomicInteger(1);
+
+        CustomThreadFactory(String featureOfFactory) {
+            PREFIX_NAME = "CustomThreadFactory: " + featureOfFactory + "-";
+        }
+
+        @Override
+        public Thread newThread(Runnable target) {
+            String name = PREFIX_NAME + id.getAndIncrement();
+            return new Thread(Thread.currentThread().getThreadGroup(), target, name, 0);
+        }
+    }
+
+    static class CustomRejectHandler implements RejectedExecutionHandler {
+        @Override
+        public void rejectedExecution(Runnable task, ThreadPoolExecutor executor) {
+            throw new RejectedExecutionException("Task " + task.toString()
+                    + " rejected from " + executor.toString());
+        }
+    }
+
+}

+ 113 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SpringContextUtils.java

@@ -0,0 +1,113 @@
+package org.jeecg.common.util;
+
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.ServiceNameConstants;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @Description: spring上下文工具类
+ * @author: jeecg-boot
+ */
+@Component
+public class SpringContextUtils implements ApplicationContextAware {
+
+	/**
+	 * 上下文对象实例
+	 */
+	private static ApplicationContext applicationContext;
+
+	@Override
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		SpringContextUtils.applicationContext = applicationContext;
+	}
+
+	/**
+	 * 获取applicationContext
+	 *
+	 * @return
+	 */
+	public static ApplicationContext getApplicationContext() {
+		return applicationContext;
+	}
+
+	/**
+	  * 获取HttpServletRequest
+	 */
+	public static HttpServletRequest getHttpServletRequest() {
+		return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+	}
+	/**
+	 * 获取HttpServletResponse
+	 */
+	public static HttpServletResponse getHttpServletResponse() {
+		return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
+	}
+
+	/**
+	*  获取项目根路径 basePath
+	*/
+	public static String getDomain(){
+		HttpServletRequest request = getHttpServletRequest();
+		StringBuffer url = request.getRequestURL();
+		//1.微服务情况下,获取gateway的basePath
+		String basePath = request.getHeader(ServiceNameConstants.X_GATEWAY_BASE_PATH);
+		if(oConvertUtils.isNotEmpty(basePath)){
+			return basePath;
+		}else{
+			String domain = url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
+			//2.【兼容】SSL认证之后,request.getScheme()获取不到https的问题
+			// https://blog.csdn.net/weixin_34376986/article/details/89767950
+			String scheme = request.getHeader(CommonConstant.X_FORWARDED_SCHEME);
+			if(scheme!=null && !request.getScheme().equals(scheme)){
+				domain = domain.replace(request.getScheme(),scheme);
+			}
+			return domain;
+		}
+	}
+
+	public static String getOrigin(){
+		HttpServletRequest request = getHttpServletRequest();
+		return request.getHeader("Origin");
+	}
+	
+	/**
+	 * 通过name获取 Bean.
+	 *
+	 * @param name
+	 * @return
+	 */
+	public static Object getBean(String name) {
+		return getApplicationContext().getBean(name);
+	}
+
+	/**
+	 * 通过class获取Bean.
+	 *
+	 * @param clazz
+	 * @param       <T>
+	 * @return
+	 */
+	public static <T> T getBean(Class<T> clazz) {
+		return getApplicationContext().getBean(clazz);
+	}
+
+	/**
+	 * 通过name,以及Clazz返回指定的Bean
+	 *
+	 * @param name
+	 * @param clazz
+	 * @param       <T>
+	 * @return
+	 */
+	public static <T> T getBean(String name, Class<T> clazz) {
+		return getApplicationContext().getBean(name, clazz);
+	}
+}

+ 421 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java

@@ -0,0 +1,421 @@
+package org.jeecg.common.util;
+
+import cn.hutool.core.util.ReUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.SymbolConstant;
+import org.jeecg.common.exception.JeecgSqlInjectionException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * sql注入处理工具类
+ * 
+ * @author zhoujf
+ */
+@Slf4j
+public class SqlInjectionUtil {	
+	/**
+	 * 默认—sql注入关键词
+	 */
+	private final static String XSS_STR = "and |exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|--";
+	/**
+	 * online报表专用—sql注入关键词
+	 */
+	private static String specialReportXssStr = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |alter |delete |grant |update |drop |master |truncate |declare |--";
+	/**
+	 * 字典专用—sql注入关键词
+	 */
+	private static String specialDictSqlXssStr = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|+|--";
+	/**
+	 * 完整匹配的key,不需要考虑前空格
+	 */
+	private static List<String> FULL_MATCHING_KEYWRODS = new ArrayList<>();
+	static {
+		FULL_MATCHING_KEYWRODS.add(";");
+		FULL_MATCHING_KEYWRODS.add("+");
+		FULL_MATCHING_KEYWRODS.add("--");
+	}
+	
+	
+	/**
+	 * sql注入风险的 正则关键字
+	 *
+	 * 函数匹配,需要用正则模式
+	 */
+	private final static String[] XSS_REGULAR_STR_ARRAY = new String[]{
+			"chr\\s*\\(",
+			"mid\\s*\\(",
+			" char\\s*\\(",
+			"sleep\\s*\\(",
+			"user\\s*\\(",
+			"show\\s+tables",
+			"user[\\s]*\\([\\s]*\\)",
+			"show\\s+databases",
+			"sleep\\(\\d*\\)",
+			"sleep\\(.*\\)",
+	};
+	/**
+	 * sql注释的正则
+	 */
+	private final static Pattern SQL_ANNOTATION = Pattern.compile("/\\*[\\s\\S]*\\*/");
+	private final static  String SQL_ANNOTATION2 = "--";
+	
+	/**
+	 * sql注入提示语
+	 */
+	private final static String SQL_INJECTION_KEYWORD_TIP = "请注意,存在SQL注入关键词---> {}";
+	private final static String SQL_INJECTION_TIP = "请注意,值可能存在SQL注入风险!--->";
+	private final static String SQL_INJECTION_TIP_VARIABLE = "请注意,值可能存在SQL注入风险!---> {}";
+	
+
+	/**
+	 * sql注入过滤处理,遇到注入关键字抛异常
+	 * @param values
+	 */
+	public static void filterContent(String... values) {
+		filterContent(values, null);
+	}
+
+	/**
+	 * 校验比较严格
+	 * 
+	 * sql注入过滤处理,遇到注入关键字抛异常
+	 *
+	 * @param value
+	 * @return
+	 */
+	public static void filterContent(String value, String customXssString) {
+		if (value == null || "".equals(value)) {
+			return;
+		}
+		// 一、校验sql注释 不允许有sql注释
+		checkSqlAnnotation(value);
+		// 转为小写进行后续比较
+		value = value.toLowerCase().trim();
+		
+		// 二、SQL注入检测存在绕过风险 (普通文本校验)
+		//https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE
+		String[] xssArr = XSS_STR.split("\\|");
+		for (int i = 0; i < xssArr.length; i++) {
+			if (value.indexOf(xssArr[i]) > -1) {
+				log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssArr[i]);
+				log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
+				throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
+			}
+		}
+		// 三、SQL注入检测存在绕过风险 (自定义传入普通文本校验)
+		if (customXssString != null) {
+			String[] xssArr2 = customXssString.split("\\|");
+			for (int i = 0; i < xssArr2.length; i++) {
+				if (value.indexOf(xssArr2[i]) > -1) {
+					log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssArr2[i]);
+					log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
+					throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
+				}
+			}
+		}
+
+		// 四、SQL注入检测存在绕过风险 (正则校验)
+		for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {
+			String regular = ".*" + regularOriginal + ".*";
+			if (Pattern.matches(regular, value)) {
+				log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, regularOriginal);
+				log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
+				throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
+			}
+		}
+		return;
+	}
+
+	/**
+	 * 判断是否存在SQL注入关键词字符串
+	 *
+	 * @param keyword
+	 * @return
+	 */
+	@SuppressWarnings("AlibabaUndefineMagicConstant")
+	private static boolean isExistSqlInjectKeyword(String sql, String keyword) {
+		if (sql.startsWith(keyword.trim())) {
+			return true;
+		} else if (sql.contains(keyword)) {
+			// 需要匹配的,sql注入关键词
+			String matchingText = " " + keyword;
+			if(FULL_MATCHING_KEYWRODS.contains(keyword)){
+				matchingText = keyword;
+			}
+			
+			if (sql.contains(matchingText)) {
+				return true;
+			} else {
+				String regularStr = "\\s+\\S+" + keyword;
+				List<String> resultFindAll = ReUtil.findAll(regularStr, sql, 0, new ArrayList<String>());
+				for (String res : resultFindAll) {
+					log.info("isExistSqlInjectKeyword —- 匹配到的SQL注入关键词:{}", res);
+					/**
+					 * SQL注入中可以替换空格的字符(%09  %0A  %0D  +都可以替代空格)
+					 * http://blog.chinaunix.net/uid-12501104-id-2932639.html
+					 * https://www.cnblogs.com/Vinson404/p/7253255.html
+					 * */
+					if (res.contains("%") || res.contains("+") || res.contains("#") || res.contains("/") || res.contains(")")) {
+						return true;
+					}
+				}
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * sql注入过滤处理,遇到注入关键字抛异常
+	 * 
+	 * @param values
+	 * @return
+	 */
+	public static void filterContent(String[] values, String customXssString) {
+		for (String val : values) {
+			if (oConvertUtils.isEmpty(val)) {
+				return;
+			}
+			filterContent(val, customXssString);
+		}
+		return;
+	}
+
+	/**
+	 * 【提醒:不通用】
+	 * 仅用于字典条件SQL参数,注入过滤
+	 *
+	 * @param value
+	 * @return
+	 */
+	public static void specialFilterContentForDictSql(String value) {
+		String[] xssArr = specialDictSqlXssStr.split("\\|");
+		if (value == null || "".equals(value)) {
+			return;
+		}
+		// 一、校验sql注释 不允许有sql注释
+		checkSqlAnnotation(value);
+		value = value.toLowerCase().trim();
+		
+		// 二、SQL注入检测存在绕过风险 (普通文本校验)
+		for (int i = 0; i < xssArr.length; i++) {
+			if (isExistSqlInjectKeyword(value, xssArr[i])) {
+				log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssArr[i]);
+				log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
+				throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
+			}
+		}
+
+		// 三、SQL注入检测存在绕过风险 (正则校验)
+		for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {
+			String regular = ".*" + regularOriginal + ".*";
+			if (Pattern.matches(regular, value)) {
+				log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, regularOriginal);
+				log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
+				throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
+			}
+		}
+		return;
+	}
+
+    /**
+	 * 【提醒:不通用】
+     *  仅用于Online报表SQL解析,注入过滤
+     * @param value
+     * @return
+     */
+	public static void specialFilterContentForOnlineReport(String value) {
+		String[] xssArr = specialReportXssStr.split("\\|");
+		if (value == null || "".equals(value)) {
+			return;
+		}
+		// 一、校验sql注释 不允许有sql注释
+		checkSqlAnnotation(value);
+		value = value.toLowerCase().trim();
+		
+		// 二、SQL注入检测存在绕过风险 (普通文本校验)
+		for (int i = 0; i < xssArr.length; i++) {
+			if (isExistSqlInjectKeyword(value, xssArr[i])) {
+				log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssArr[i]);
+				log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
+				throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
+			}
+		}
+
+		// 三、SQL注入检测存在绕过风险 (正则校验)
+		for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {
+			String regular = ".*" + regularOriginal + ".*";
+			if (Pattern.matches(regular, value)) {
+				log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, regularOriginal);
+				log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
+				throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
+			}
+		}
+		return;
+	}
+
+
+	/**
+	 * 校验是否有sql注释 
+	 * @return
+	 */
+	public static void checkSqlAnnotation(String str){
+		if(str.contains(SQL_ANNOTATION2)){
+			String error = "请注意,SQL中不允许含注释,有安全风险!";
+			log.error(error);
+			throw new RuntimeException(error);
+		}
+
+		
+		Matcher matcher = SQL_ANNOTATION.matcher(str);
+		if(matcher.find()){
+			String error = "请注意,值可能存在SQL注入风险---> \\*.*\\";
+			log.error(error);
+			throw new JeecgSqlInjectionException(error);
+		}
+	}
+
+
+	/**
+	 * 返回查询表名
+	 * <p>
+	 * sql注入过滤处理,遇到注入关键字抛异常
+	 *
+	 * @param table
+	 */
+	private static Pattern tableNamePattern = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_\\$]{0,63}$");
+	public static String getSqlInjectTableName(String table) {
+		if(oConvertUtils.isEmpty(table)){
+			return table;
+		}
+		
+		table = table.trim();
+		/**
+		 * 检验表名是否合法
+		 *
+		 * 表名只能由字母、数字和下划线组成。
+		 * 表名必须以字母开头。
+		 * 表名长度通常有限制,例如最多为 64 个字符。
+		 */
+		boolean isValidTableName = tableNamePattern.matcher(table).matches();
+		if (!isValidTableName) {
+			String errorMsg = "表名不合法,存在SQL注入风险!--->" + table;
+			log.error(errorMsg);
+			throw new JeecgSqlInjectionException(errorMsg);
+		}
+
+		//进一步验证是否存在SQL注入风险
+		filterContent(table);
+		return table;
+	}
+
+
+	/**
+	 * 返回查询字段
+	 * <p>
+	 * sql注入过滤处理,遇到注入关键字抛异常
+	 *
+	 * @param field
+	 */
+	static final Pattern fieldPattern = Pattern.compile("^[a-zA-Z0-9_]+$");
+	public static String getSqlInjectField(String field) {
+		if(oConvertUtils.isEmpty(field)){
+			return field;
+		}
+		
+		field = field.trim();
+
+		if (field.contains(SymbolConstant.COMMA)) {
+			return getSqlInjectField(field.split(SymbolConstant.COMMA));
+		}
+
+		/**
+		 * 校验表字段是否有效
+		 *
+		 * 字段定义只能是是字母 数字 下划线的组合(不允许有空格、转义字符串等)
+		 */
+		boolean isValidField = fieldPattern.matcher(field).matches();
+		if (!isValidField) {
+			String errorMsg = "字段不合法,存在SQL注入风险!--->" + field;
+			log.error(errorMsg);
+			throw new JeecgSqlInjectionException(errorMsg);
+		}
+
+		//进一步验证是否存在SQL注入风险
+		filterContent(field);
+		return field;
+	}
+
+	/**
+	 * 获取多个字段
+	 * 返回: 逗号拼接
+	 *
+	 * @param fields
+	 * @return
+	 */
+	public static String getSqlInjectField(String... fields) {
+		for (String s : fields) {
+			getSqlInjectField(s);
+		}
+		return String.join(SymbolConstant.COMMA, fields);
+	}
+
+
+	/**
+	 * 获取排序字段
+	 * 返回:字符串
+	 *
+	 * 1.将驼峰命名转化成下划线 
+	 * 2.限制sql注入
+	 * @param sortField  排序字段
+	 * @return
+	 */
+	public static String getSqlInjectSortField(String sortField) {
+		String field = SqlInjectionUtil.getSqlInjectField(oConvertUtils.camelToUnderline(sortField));
+		return field;
+	}
+
+	/**
+	 * 获取多个排序字段
+	 * 返回:数组
+	 *
+	 * 1.将驼峰命名转化成下划线 
+	 * 2.限制sql注入
+	 * @param sortFields 多个排序字段
+	 * @return
+	 */
+	public static List getSqlInjectSortFields(String... sortFields) {
+		List list = new ArrayList<String>();
+		for (String sortField : sortFields) {
+			list.add(getSqlInjectSortField(sortField));
+		}
+		return list;
+	}
+
+	/**
+	 * 获取 orderBy type
+	 * 返回:字符串
+	 * <p>
+	 * 1.检测是否为 asc 或 desc 其中的一个
+	 * 2.限制sql注入
+	 *
+	 * @param orderType
+	 * @return
+	 */
+	public static String getSqlInjectOrderType(String orderType) {
+		if (orderType == null) {
+			return null;
+		}
+		orderType = orderType.trim();
+		if (CommonConstant.ORDER_TYPE_ASC.equalsIgnoreCase(orderType)) {
+			return CommonConstant.ORDER_TYPE_ASC;
+		} else {
+			return CommonConstant.ORDER_TYPE_DESC;
+		}
+	}
+
+}

+ 168 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java

@@ -0,0 +1,168 @@
+package org.jeecg.common.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.jeecg.common.api.CommonAPI;
+import org.jeecg.common.constant.CacheConstant;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.desensitization.util.SensitiveInfoUtil;
+import org.jeecg.common.exception.JeecgBoot401Exception;
+import org.jeecg.common.system.util.JwtUtil;
+import org.jeecg.common.system.vo.LoginUser;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @Author scott
+ * @Date 2019/9/23 14:12
+ * @Description: 编程校验token有效性
+ */
+@Slf4j
+public class TokenUtils {
+
+    /**
+     * 获取 request 里传递的 token
+     *
+     * @param request
+     * @return
+     */
+    public static String getTokenByRequest(HttpServletRequest request) {
+        String token = request.getParameter("token");
+        if (token == null) {
+            token = request.getHeader("X-Access-Token");
+        }
+        return token;
+    }
+
+    /**
+     * 获取 request 里传递的 tenantId (租户ID)
+     * 积木报表
+     */
+    public static String getTenantIdByRequest(HttpServletRequest request) {
+        // String tenantId = request.getParameter("tenantId");
+        // if (tenantId == null) {
+        //     tenantId = oConvertUtils.getString(request.getHeader("X-Tenant-Id"));
+        // }
+        // return tenantId;
+        return "0";
+    }
+
+    /**
+     * 获取 request 里传递的 lowAppId (低代码应用ID)
+     * 积木报表
+     */
+    public static String getLowAppIdByRequest(HttpServletRequest request) {
+        // String lowAppId = request.getParameter("X-Low-App-ID");
+        // if (lowAppId == null) {
+        //     lowAppId = oConvertUtils.getString(request.getHeader("X-Low-App-ID"));
+        // }
+        // return lowAppId;
+        return "0";
+    }
+
+    /**
+     * 获取 request 里传递的 token
+     *
+     * @return
+     */
+    public static String getTokenByRequest() {
+        String token = null;
+        try {
+            HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
+            token = TokenUtils.getTokenByRequest(request);
+        } catch (Exception e) {
+            // e.printStackTrace();
+        }
+        return token;
+    }
+
+    /**
+     * 验证Token
+     */
+    public static boolean verifyToken(HttpServletRequest request, CommonAPI commonApi, RedisUtil redisUtil) {
+        log.debug(" -- url --" + request.getRequestURL());
+        String token = getTokenByRequest(request);
+        return TokenUtils.verifyToken(token, commonApi, redisUtil);
+    }
+
+    /**
+     * 验证Token
+     */
+    public static boolean verifyToken(String token, CommonAPI commonApi, RedisUtil redisUtil) {
+        if (StringUtils.isBlank(token)) {
+            throw new JeecgBoot401Exception("token不能为空!");
+        }
+
+        // 解密获得username,用于和数据库进行对比
+        String username = JwtUtil.getUsername(token);
+        if (username == null) {
+            throw new JeecgBoot401Exception("token非法无效!");
+        }
+
+        // 查询用户信息
+        LoginUser user = TokenUtils.getLoginUser(username, commonApi, redisUtil);
+        // LoginUser user = commonApi.getUserByName(username);
+        if (user == null) {
+            throw new JeecgBoot401Exception("用户不存在!");
+        }
+        // 判断用户状态
+        if (user.getStatus() != 1) {
+            throw new JeecgBoot401Exception("账号已被锁定,请联系管理员!");
+        }
+        // 校验token是否超时失效 & 或者账号密码是否错误
+        if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {
+            throw new JeecgBoot401Exception(CommonConstant.TOKEN_IS_INVALID_MSG);
+        }
+        return true;
+    }
+
+    /**
+     * 刷新token(保证用户在线操作不掉线)
+     *
+     * @param token
+     * @param userName
+     * @param passWord
+     * @param redisUtil
+     * @return
+     */
+    private static boolean jwtTokenRefresh(String token, String userName, String passWord, RedisUtil redisUtil) {
+        String cacheToken = oConvertUtils.getString(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));
+        if (oConvertUtils.isNotEmpty(cacheToken)) {
+            // 校验token有效性
+            if (!JwtUtil.verify(cacheToken, userName, passWord)) {
+                String newAuthorization = JwtUtil.sign(userName, passWord);
+                // 设置Toekn缓存有效时间
+                redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
+                redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 获取登录用户
+     *
+     * @param commonApi
+     * @param username
+     * @return
+     */
+    public static LoginUser getLoginUser(String username, CommonAPI commonApi, RedisUtil redisUtil) {
+        LoginUser loginUser = null;
+        String loginUserKey = CacheConstant.SYS_USERS_CACHE + "::" + username;
+        //【重要】此处通过redis原生获取缓存用户,是为了解决微服务下system服务挂了,其他服务互调不通问题---
+        if (redisUtil.hasKey(loginUserKey)) {
+            try {
+                loginUser = (LoginUser) redisUtil.get(loginUserKey);
+                // 解密用户
+                SensitiveInfoUtil.handlerObject(loginUser, false);
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        } else {
+            // 查询用户信息
+            loginUser = commonApi.getUserByName(username);
+        }
+        return loginUser;
+    }
+}

+ 96 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/UUIDGenerator.java

@@ -0,0 +1,96 @@
+package org.jeecg.common.util;
+
+
+import java.net.InetAddress;
+
+/**
+ * 
+ * @Author  张代浩
+ *
+ */
+public class UUIDGenerator {
+
+	
+	/**
+	 * 产生一个32位的UUID
+	 * 
+	 * @return
+	 */
+
+	public static String generate() {
+		return new StringBuilder(32).append(format(getIp())).append(
+				format(getJvm())).append(format(getHiTime())).append(
+				format(getLoTime())).append(format(getCount())).toString();
+		
+	}
+
+	private static final int IP;
+	static {
+		int ipadd;
+		try {
+			ipadd = toInt(InetAddress.getLocalHost().getAddress());
+		} catch (Exception e) {
+			ipadd = 0;
+		}
+		IP = ipadd;
+	}
+
+	private static short counter = (short) 0;
+
+	private static final int JVM = (int) (System.currentTimeMillis() >>> 8);
+
+	private final static String format(int intval) {
+		String formatted = Integer.toHexString(intval);
+		StringBuilder buf = new StringBuilder("00000000");
+		buf.replace(8 - formatted.length(), 8, formatted);
+		return buf.toString();
+	}
+
+	private final static String format(short shortval) {
+		String formatted = Integer.toHexString(shortval);
+		StringBuilder buf = new StringBuilder("0000");
+		buf.replace(4 - formatted.length(), 4, formatted);
+		return buf.toString();
+	}
+
+	private final static int getJvm() {
+		return JVM;
+	}
+
+	private final static short getCount() {
+		synchronized (UUIDGenerator.class) {
+			if (counter < 0) {
+				counter = 0;
+			}
+			return counter++;
+		}
+	}
+
+	/**
+	 * Unique in a local network
+	 */
+	private final static int getIp() {
+		return IP;
+	}
+
+	/**
+	 * Unique down to millisecond
+	 */
+	private final static short getHiTime() {
+		return (short) (System.currentTimeMillis() >>> 32);
+	}
+
+	private final static int getLoTime() {
+		return (int) System.currentTimeMillis();
+	}
+
+	private final static int toInt(byte[] bytes) {
+		int result = 0;
+		int length = 4;
+		for (int i = 0; i < length; i++) {
+			result = (result << 8) - Byte.MIN_VALUE + (int) bytes[i];
+		}
+		return result;
+	}
+
+}

+ 176 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/YouBianCodeUtil.java

@@ -0,0 +1,176 @@
+package org.jeecg.common.util;
+
+import io.netty.util.internal.StringUtil;
+
+/**
+ * 流水号生成规则(按默认规则递增,数字从1-99开始递增,数字到99,递增字母;位数不够增加位数)
+ * A001
+ * A001A002
+ * @Author zhangdaihao
+ *
+ */
+public class YouBianCodeUtil {
+
+	// 数字位数(默认生成3位的数字)
+
+    /**代表数字位数*/
+	private static final int NUM_LENGTH = 2;
+
+	public static final int ZHANWEI_LENGTH = 1+ NUM_LENGTH;
+
+	public static final char LETTER= 'Z';
+
+	/**
+	 * 根据前一个code,获取同级下一个code
+	 * 例如:当前最大code为D01A04,下一个code为:D01A05
+	 *
+	 * @param code
+	 * @return
+	 */
+	public static synchronized String getNextYouBianCode(String code) {
+		String newcode = "";
+		if (oConvertUtils.isEmpty(code)) {
+			String zimu = "A";
+			String num = getStrNum(1);
+			newcode = zimu + num;
+		} else {
+			String beforeCode = code.substring(0, code.length() - 1- NUM_LENGTH);
+			String afterCode = code.substring(code.length() - 1 - NUM_LENGTH,code.length());
+			char afterCodeZimu = afterCode.substring(0, 1).charAt(0);
+			Integer afterCodeNum = Integer.parseInt(afterCode.substring(1));
+//			org.jeecgframework.core.util.LogUtil.info(after_code);
+//			org.jeecgframework.core.util.LogUtil.info(after_code_zimu);
+//			org.jeecgframework.core.util.LogUtil.info(after_code_num);
+
+			String nextNum = "";
+			char nextZimu = 'A';
+			// 先判断数字等于999*,则计数从1重新开始,递增
+			if (afterCodeNum == getMaxNumByLength(NUM_LENGTH)) {
+				nextNum = getNextStrNum(0);
+			} else {
+				nextNum = getNextStrNum(afterCodeNum);
+			}
+			// 先判断数字等于999*,则字母从A重新开始,递增
+			if(afterCodeNum == getMaxNumByLength(NUM_LENGTH)) {
+				nextZimu = getNextZiMu(afterCodeZimu);
+			}else{
+				nextZimu = afterCodeZimu;
+			}
+
+			// 例如Z99,下一个code就是Z99A01
+			if (LETTER == afterCodeZimu && getMaxNumByLength(NUM_LENGTH) == afterCodeNum) {
+				newcode = code + (nextZimu + nextNum);
+			} else {
+				newcode = beforeCode + (nextZimu + nextNum);
+			}
+		}
+		return newcode;
+
+	}
+
+	/**
+	 * 根据父亲code,获取下级的下一个code
+	 *
+	 * 例如:父亲CODE:A01
+	 *       当前CODE:A01B03
+	 *       获取的code:A01B04
+	 *
+	 * @param parentCode   上级code
+	 * @param localCode    同级code
+	 * @return
+	 */
+	public static synchronized String getSubYouBianCode(String parentCode,String localCode) {
+		if(localCode!=null && localCode!=""){
+
+//			return parentCode + getNextYouBianCode(localCode);
+			return getNextYouBianCode(localCode);
+
+		}else{
+			parentCode = parentCode + "A"+ getNextStrNum(0);
+		}
+		return parentCode;
+	}
+
+
+
+	/**
+	 * 将数字前面位数补零
+	 *
+	 * @param num
+	 * @return
+	 */
+	private static String getNextStrNum(int num) {
+		return getStrNum(getNextNum(num));
+	}
+
+	/**
+	 * 将数字前面位数补零
+	 *
+	 * @param num
+	 * @return
+	 */
+	private static String getStrNum(int num) {
+		String s = String.format("%0" + NUM_LENGTH + "d", num);
+		return s;
+	}
+
+	/**
+	 * 递增获取下个数字
+	 *
+	 * @param num
+	 * @return
+	 */
+	private static int getNextNum(int num) {
+		num++;
+		return num;
+	}
+
+	/**
+	 * 递增获取下个字母
+	 *
+	 * @param zimu
+	 * @return
+	 */
+	private static char getNextZiMu(char zimu) {
+		if (zimu == LETTER) {
+			return 'A';
+		}
+		zimu++;
+		return zimu;
+	}
+
+	/**
+	 * 根据数字位数获取最大值
+	 * @param length
+	 * @return
+	 */
+	private static int getMaxNumByLength(int length){
+		if(length==0){
+			return 0;
+		}
+        StringBuilder maxNum = new StringBuilder();
+		for (int i=0;i<length;i++){
+            maxNum.append("9");
+		}
+		return Integer.parseInt(maxNum.toString());
+	}
+	public static String[] cutYouBianCode(String code){
+		if(code==null || StringUtil.isNullOrEmpty(code)){
+			return null;
+		}else{
+			//获取标准长度为numLength+1,截取的数量为code.length/numLength+1
+			int c = code.length()/(NUM_LENGTH +1);
+			String[] cutcode = new String[c];
+			for(int i =0 ; i <c;i++){
+				cutcode[i] = code.substring(0,(i+1)*(NUM_LENGTH +1));
+			}
+			return cutcode;
+		}
+
+	}
+//	public static void main(String[] args) {
+//		// org.jeecgframework.core.util.LogUtil.info(getNextZiMu('C'));
+//		// org.jeecgframework.core.util.LogUtil.info(getNextNum(8));
+//	    // org.jeecgframework.core.util.LogUtil.info(cutYouBianCode("C99A01B01")[2]);
+//	}
+}

+ 122 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/DbTypeUtils.java

@@ -0,0 +1,122 @@
+package org.jeecg.common.util.dynamic.db;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import org.jeecg.common.constant.DataBaseConstant;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 数据库类型判断
+ * 【有些数据库引擎是一样的,以达到复用目的】
+ * @author: jeecg-boot
+ */
+public class DbTypeUtils {
+
+    public static Map<String, String> dialectMap = new HashMap<String, String>();
+    static{
+        dialectMap.put("mysql", "org.hibernate.dialect.MySQL5InnoDBDialect");
+        // mariadb数据库 1  --
+        dialectMap.put("mariadb", "org.hibernate.dialect.MariaDBDialect");
+        //oracle数据库 1
+        dialectMap.put("oracle", "org.hibernate.dialect.OracleDialect");
+        //oracle数据库
+        dialectMap.put("oracle12c", "org.hibernate.dialect.OracleDialect");
+        // db2数据库 1xx
+        dialectMap.put("db2", "org.hibernate.dialect.DB2390Dialect");
+        // H2数据库
+        dialectMap.put("h2", "org.hibernate.dialect.HSQLDialect");
+        // HSQL数据库  1
+        dialectMap.put("hsql", "org.hibernate.dialect.HSQLDialect");
+        //SQLite数据库 应用平台mobile
+        dialectMap.put("sqlite", "org.jeecg.modules.online.config.dialect.SQLiteDialect");
+        //PostgreSQL数据库1  --
+        dialectMap.put("postgresql", "org.hibernate.dialect.PostgreSQLDialect");
+        dialectMap.put("sqlserver2005", "org.hibernate.dialect.SQLServer2005Dialect");
+        //sqlserver数据库1
+        dialectMap.put("sqlserver", "org.hibernate.dialect.SQLServerDialect");
+        //达梦数据库 [国产] 1--
+        dialectMap.put("dm", "org.hibernate.dialect.DmDialect");
+        //虚谷数据库
+        dialectMap.put("xugu", "org.hibernate.dialect.HSQLDialect");
+        //人大金仓 [国产] 1
+        dialectMap.put("kingbasees", "org.hibernate.dialect.PostgreSQLDialect");
+        // Phoenix HBase数据库
+        dialectMap.put("phoenix", "org.hibernate.dialect.HSQLDialect");
+        // Gauss 数据库
+        dialectMap.put("zenith", "org.hibernate.dialect.PostgreSQLDialect");
+        //阿里云PolarDB
+        dialectMap.put("clickhouse", "org.hibernate.dialect.MySQLDialect");
+        // 南大通用数据库
+        dialectMap.put("gbase", "org.hibernate.dialect.PostgreSQLDialect");
+        //神通数据库 [国产]
+        dialectMap.put("oscar", "org.hibernate.dialect.PostgreSQLDialect");
+        //Sybase ASE 数据库
+        dialectMap.put("sybase", "org.hibernate.dialect.SybaseDialect");
+        dialectMap.put("oceanbase", "org.hibernate.dialect.PostgreSQLDialect");
+        dialectMap.put("Firebird", "org.hibernate.dialect.FirebirdDialect");
+        //瀚高数据库
+        dialectMap.put("highgo", "org.hibernate.dialect.HSQLDialect");
+        dialectMap.put("other", "org.hibernate.dialect.PostgreSQLDialect");
+    }
+
+    public static boolean dbTypeIsMySql(DbType dbType) {
+        return dbTypeIf(dbType, DbType.MYSQL, DbType.MARIADB, DbType.CLICK_HOUSE, DbType.SQLITE);
+    }
+
+    public static boolean dbTypeIsOracle(DbType dbType) {
+        return dbTypeIf(dbType, DbType.ORACLE, DbType.ORACLE_12C, DbType.DM);
+    }
+
+    public static boolean dbTypeIsSqlServer(DbType dbType) {
+        return dbTypeIf(dbType, DbType.SQL_SERVER, DbType.SQL_SERVER2005);
+    }
+
+    public static boolean dbTypeIsPostgre(DbType dbType) {
+        return dbTypeIf(dbType, DbType.POSTGRE_SQL, DbType.KINGBASE_ES, DbType.GAUSS);
+    }
+
+
+
+    /**
+     *  根据枚举类 获取数据库类型的字符串
+     * @param dbType
+     * @return
+     */
+    public static String getDbTypeString(DbType dbType){
+        if(DbType.DB2.equals(dbType)){
+            return DataBaseConstant.DB_TYPE_DB2;
+        }else if(DbType.HSQL.equals(dbType)){
+            return DataBaseConstant.DB_TYPE_HSQL;
+        }else if(dbTypeIsOracle(dbType)){
+            return DataBaseConstant.DB_TYPE_ORACLE;
+        }else if(dbTypeIsSqlServer(dbType)){
+            return DataBaseConstant.DB_TYPE_SQLSERVER;
+        }else if(dbTypeIsPostgre(dbType)){
+            return DataBaseConstant.DB_TYPE_POSTGRESQL;
+        }
+        return DataBaseConstant.DB_TYPE_MYSQL;
+    }
+
+    /**
+     *  根据枚举类 获取数据库方言字符串
+     * @param dbType
+     * @return
+     */
+    public static String getDbDialect(DbType dbType){
+        return dialectMap.get(dbType.getDb());
+    }
+
+    /**
+     * 判断数据库类型
+     */
+    public static boolean dbTypeIf(DbType dbType, DbType... correctTypes) {
+        for (DbType type : correctTypes) {
+            if (type.equals(dbType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}

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