Browse Source

Initial commit

chunping 3 years ago
commit
e7fb093d4e
100 changed files with 10171 additions and 0 deletions
  1. 52 0
      .gitignore
  2. 20 0
      LICENSE
  3. 1 0
      README.md
  4. 134 0
      mp-admin/.factorypath
  5. 113 0
      mp-admin/pom.xml
  6. 50 0
      mp-admin/src/main/java/com/qs/mp/MpApplication.java
  7. 18 0
      mp-admin/src/main/java/com/qs/mp/MpServletInitializer.java
  8. 58 0
      mp-admin/src/main/java/com/qs/mp/handler/BodyReaderHttpServletRequestWrapper.java
  9. 132 0
      mp-admin/src/main/java/com/qs/mp/handler/HttpHelper.java
  10. 122 0
      mp-admin/src/main/java/com/qs/mp/handler/auth/SignInterceptor.java
  11. 47 0
      mp-admin/src/main/java/com/qs/mp/handler/auth/SignUtils.java
  12. 45 0
      mp-admin/src/main/java/com/qs/mp/handler/auth/WebConfigurer.java
  13. 319 0
      mp-admin/src/main/java/com/qs/mp/web/controller/BaseController.java
  14. 126 0
      mp-admin/src/main/java/com/qs/mp/web/controller/api/callback/PayCallBackController.java
  15. 133 0
      mp-admin/src/main/java/com/qs/mp/web/controller/api/callback/WxCallBackController.java
  16. 76 0
      mp-admin/src/main/java/com/qs/mp/web/controller/api/sms/SmsController.java
  17. 255 0
      mp-admin/src/main/java/com/qs/mp/web/controller/api/user/UserController.java
  18. 44 0
      mp-admin/src/main/java/com/qs/mp/web/controller/common/BaseApiController.java
  19. 96 0
      mp-admin/src/main/java/com/qs/mp/web/controller/common/CaptchaController.java
  20. 118 0
      mp-admin/src/main/java/com/qs/mp/web/controller/common/CommonController.java
  21. 94 0
      mp-admin/src/main/java/com/qs/mp/web/controller/common/FileDownloadController.java
  22. 199 0
      mp-admin/src/main/java/com/qs/mp/web/controller/common/FileUploadController.java
  23. 53 0
      mp-admin/src/main/java/com/qs/mp/web/controller/monitor/CacheController.java
  24. 27 0
      mp-admin/src/main/java/com/qs/mp/web/controller/monitor/ServerController.java
  25. 67 0
      mp-admin/src/main/java/com/qs/mp/web/controller/monitor/SysLogininforController.java
  26. 67 0
      mp-admin/src/main/java/com/qs/mp/web/controller/monitor/SysOperlogController.java
  27. 92 0
      mp-admin/src/main/java/com/qs/mp/web/controller/monitor/SysUserOnlineController.java
  28. 167 0
      mp-admin/src/main/java/com/qs/mp/web/controller/quartz/SysJobController.java
  29. 90 0
      mp-admin/src/main/java/com/qs/mp/web/controller/quartz/SysJobLogController.java
  30. 137 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysConfigController.java
  31. 165 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysDeptController.java
  32. 122 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysDictDataController.java
  33. 132 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysDictTypeController.java
  34. 29 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysIndexController.java
  35. 125 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysLoginController.java
  36. 143 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysMenuController.java
  37. 92 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysNoticeController.java
  38. 130 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysPostController.java
  39. 300 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysProfileController.java
  40. 38 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysRegisterController.java
  41. 257 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysRoleController.java
  42. 231 0
      mp-admin/src/main/java/com/qs/mp/web/controller/system/SysUserController.java
  43. 24 0
      mp-admin/src/main/java/com/qs/mp/web/controller/tool/SwaggerController.java
  44. 181 0
      mp-admin/src/main/java/com/qs/mp/web/controller/tool/TestController.java
  45. 9 0
      mp-admin/src/main/java/com/qs/mp/web/core/common/ErrorConstants.java
  46. 125 0
      mp-admin/src/main/java/com/qs/mp/web/core/config/SwaggerConfig.java
  47. 1 0
      mp-admin/src/main/resources/META-INF/spring-devtools.properties
  48. 126 0
      mp-admin/src/main/resources/application-8100.yml
  49. 126 0
      mp-admin/src/main/resources/application-8200.yml
  50. 120 0
      mp-admin/src/main/resources/application-dev.yml
  51. 124 0
      mp-admin/src/main/resources/application-test.yml
  52. 134 0
      mp-admin/src/main/resources/application.yml
  53. 24 0
      mp-admin/src/main/resources/banner.txt
  54. 37 0
      mp-admin/src/main/resources/i18n/messages.properties
  55. 66 0
      mp-admin/src/main/resources/logback-8090.xml
  56. 70 0
      mp-admin/src/main/resources/logback-8100.xml
  57. 70 0
      mp-admin/src/main/resources/logback-8200.xml
  58. 66 0
      mp-admin/src/main/resources/logback-spring.xml
  59. 15 0
      mp-admin/src/main/resources/mybatis/mybatis-config.xml
  60. 103 0
      mp-common/.factorypath
  61. 198 0
      mp-common/pom.xml
  62. 28 0
      mp-common/src/main/java/com/qs/mp/common/annotation/DataScope.java
  63. 28 0
      mp-common/src/main/java/com/qs/mp/common/annotation/DataSource.java
  64. 170 0
      mp-common/src/main/java/com/qs/mp/common/annotation/Excel.java
  65. 18 0
      mp-common/src/main/java/com/qs/mp/common/annotation/Excels.java
  66. 41 0
      mp-common/src/main/java/com/qs/mp/common/annotation/Log.java
  67. 40 0
      mp-common/src/main/java/com/qs/mp/common/annotation/RateLimiter.java
  68. 23 0
      mp-common/src/main/java/com/qs/mp/common/annotation/RepeatSubmit.java
  69. 124 0
      mp-common/src/main/java/com/qs/mp/common/config/MpConfig.java
  70. 156 0
      mp-common/src/main/java/com/qs/mp/common/constant/Constants.java
  71. 114 0
      mp-common/src/main/java/com/qs/mp/common/constant/GenConstants.java
  72. 89 0
      mp-common/src/main/java/com/qs/mp/common/constant/HttpStatus.java
  73. 50 0
      mp-common/src/main/java/com/qs/mp/common/constant/ScheduleConstants.java
  74. 78 0
      mp-common/src/main/java/com/qs/mp/common/constant/UserConstants.java
  75. 314 0
      mp-common/src/main/java/com/qs/mp/common/core/domain/AjaxResult.java
  76. 114 0
      mp-common/src/main/java/com/qs/mp/common/core/domain/BaseEntity.java
  77. 79 0
      mp-common/src/main/java/com/qs/mp/common/core/domain/TreeEntity.java
  78. 82 0
      mp-common/src/main/java/com/qs/mp/common/core/domain/model/LoginBody.java
  79. 11 0
      mp-common/src/main/java/com/qs/mp/common/core/domain/model/RegisterBody.java
  80. 9 0
      mp-common/src/main/java/com/qs/mp/common/core/mapper/BaseMapper.java
  81. 50 0
      mp-common/src/main/java/com/qs/mp/common/core/mapper/BaseMetaObjectHandler.java
  82. 84 0
      mp-common/src/main/java/com/qs/mp/common/core/page/PageDomain.java
  83. 85 0
      mp-common/src/main/java/com/qs/mp/common/core/page/TableDataInfo.java
  84. 49 0
      mp-common/src/main/java/com/qs/mp/common/core/page/TableSupport.java
  85. 234 0
      mp-common/src/main/java/com/qs/mp/common/core/redis/RedisCache.java
  86. 12 0
      mp-common/src/main/java/com/qs/mp/common/core/service/BaseService.java
  87. 231 0
      mp-common/src/main/java/com/qs/mp/common/core/service/BaseServiceImpl.java
  88. 30 0
      mp-common/src/main/java/com/qs/mp/common/core/sign/SignParam.java
  89. 86 0
      mp-common/src/main/java/com/qs/mp/common/core/text/CharsetKit.java
  90. 1005 0
      mp-common/src/main/java/com/qs/mp/common/core/text/Convert.java
  91. 92 0
      mp-common/src/main/java/com/qs/mp/common/core/text/StrFormatter.java
  92. 20 0
      mp-common/src/main/java/com/qs/mp/common/enums/BusinessStatus.java
  93. 59 0
      mp-common/src/main/java/com/qs/mp/common/enums/BusinessType.java
  94. 19 0
      mp-common/src/main/java/com/qs/mp/common/enums/DataSourceType.java
  95. 51 0
      mp-common/src/main/java/com/qs/mp/common/enums/ErrorCodeEnum.java
  96. 36 0
      mp-common/src/main/java/com/qs/mp/common/enums/HttpMethod.java
  97. 20 0
      mp-common/src/main/java/com/qs/mp/common/enums/LimitType.java
  98. 24 0
      mp-common/src/main/java/com/qs/mp/common/enums/OperatorType.java
  99. 27 0
      mp-common/src/main/java/com/qs/mp/common/enums/PayOrderTypeEnum.java
  100. 54 0
      mp-common/src/main/java/com/qs/mp/common/enums/RoleTypeEnum.java

+ 52 - 0
.gitignore

@@ -0,0 +1,52 @@
+target/
+.settings/
+*.DS_Store
+*.DS_Store/
+.Spotlight-V100
+.Trashes
+Icon?
+ehthumbs.db
+Thumbs.db
+
+.project
+
+*/*.classpath
+
+.metadata
+.recommenders
+.externalToolBuilders
+.tomcatplugin
+Servers/
+
+.classpath
+*.classpath
+eepay-admin/.classpath
+
+mvnw
+mvnw.cmd
+
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+*.iml
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+.idea

+ 20 - 0
LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2018 ygp
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 1 - 0
README.md

@@ -0,0 +1 @@
+云管铺

+ 134 - 0
mp-admin/.factorypath

@@ -0,0 +1,134 @@
+<factorypath>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-devtools/2.2.13.RELEASE/spring-boot-devtools-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot/2.2.13.RELEASE/spring-boot-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-core/5.2.12.RELEASE/spring-core-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jcl/5.2.12.RELEASE/spring-jcl-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context/5.2.12.RELEASE/spring-context-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-expression/5.2.12.RELEASE/spring-expression-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-autoconfigure/2.2.13.RELEASE/spring-boot-autoconfigure-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-boot-starter/3.0.0/springfox-boot-starter-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-oas/3.0.0/springfox-oas-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-annotations/2.1.2/swagger-annotations-2.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-models/2.1.2/swagger-models-2.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-spi/3.0.0/springfox-spi-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-schema/3.0.0/springfox-schema-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-core/3.0.0/springfox-core-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/net/bytebuddy/byte-buddy/1.10.19/byte-buddy-1.10.19.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-spring-web/3.0.0/springfox-spring-web-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/github/classgraph/classgraph/4.8.83/classgraph-4.8.83.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-spring-webmvc/3.0.0/springfox-spring-webmvc-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-spring-webflux/3.0.0/springfox-spring-webflux-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-swagger-common/3.0.0/springfox-swagger-common-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mapstruct/mapstruct/1.3.1.Final/mapstruct-1.3.1.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-data-rest/3.0.0/springfox-data-rest-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-bean-validators/3.0.0/springfox-bean-validators-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-swagger2/3.0.0/springfox-swagger2-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-swagger-ui/3.0.0/springfox-swagger-ui-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/plugin/spring-plugin-core/2.0.0.RELEASE/spring-plugin-core-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-beans/5.2.12.RELEASE/spring-beans-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aop/5.2.12.RELEASE/spring-aop-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/plugin/spring-plugin-metadata/2.0.0.RELEASE/spring-plugin-metadata-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/swagger-models/1.6.2/swagger-models-1.6.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.10.5/jackson-annotations-2.10.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/swagger-annotations/1.6.2/swagger-annotations-1.6.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/mysql/mysql-connector-java/8.0.22/mysql-connector-java-8.0.22.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-web/2.2.13.RELEASE/spring-boot-starter-web-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter/2.2.13.RELEASE/spring-boot-starter-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-logging/2.2.13.RELEASE/spring-boot-starter-logging-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-to-slf4j/2.12.1/log4j-to-slf4j-2.12.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-json/2.2.13.RELEASE/spring-boot-starter-json-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.5/jackson-datatype-jdk8-2.10.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.5/jackson-datatype-jsr310-2.10.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.5/jackson-module-parameter-names-2.10.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-tomcat/2.2.13.RELEASE/spring-boot-starter-tomcat-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-core/9.0.41/tomcat-embed-core-9.0.41.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-el/9.0.41/tomcat-embed-el-9.0.41.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.41/tomcat-embed-websocket-9.0.41.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-validation/2.2.13.RELEASE/spring-boot-starter-validation-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/hibernate/validator/hibernate-validator/6.0.22.Final/hibernate-validator-6.0.22.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-web/5.2.12.RELEASE/spring-web-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-webmvc/5.2.12.RELEASE/spring-webmvc-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-aop/2.2.13.RELEASE/spring-boot-starter-aop-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/aspectj/aspectjweaver/1.9.6/aspectjweaver-1.9.6.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/alibaba/druid-spring-boot-starter/1.2.6/druid-spring-boot-starter-1.2.6.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/alibaba/druid/1.2.6/druid-1.2.6.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/penggle/kaptcha/2.3.2/kaptcha-2.3.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/jhlabs/filters/2.0.235-1/filters-2.0.235-1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/oshi/oshi-core/5.8.0/oshi-core-5.8.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/net/java/dev/jna/jna/5.8.0/jna-5.8.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/net/java/dev/jna/jna-platform/5.8.0/jna-platform-5.8.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/quartz-scheduler/quartz/2.3.2/quartz-2.3.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/mchange/mchange-commons-java/0.2.15/mchange-commons-java-0.2.15.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context-support/5.2.12.RELEASE/spring-context-support-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-security/2.2.13.RELEASE/spring-boot-starter-security-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-config/5.2.8.RELEASE/spring-security-config-5.2.8.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-core/5.2.8.RELEASE/spring-security-core-5.2.8.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-web/5.2.8.RELEASE/spring-security-web-5.2.8.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/pagehelper/pagehelper-spring-boot-starter/1.3.1/pagehelper-spring-boot-starter-1.3.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/spring/boot/mybatis-spring-boot-starter/2.1.4/mybatis-spring-boot-starter-2.1.4.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.1.4/mybatis-spring-boot-autoconfigure-2.1.4.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/mybatis/3.5.6/mybatis-3.5.6.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/pagehelper/pagehelper-spring-boot-autoconfigure/1.3.1/pagehelper-spring-boot-autoconfigure-1.3.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/pagehelper/pagehelper/5.2.1/pagehelper-5.2.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/jsqlparser/jsqlparser/4.0/jsqlparser-4.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.10.5.1/jackson-databind-2.10.5.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.10.5/jackson-core-2.10.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/alibaba/fastjson/1.2.76/fastjson-1.2.76.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/commons-fileupload/commons-fileupload/1.4/commons-fileupload-1.4.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/poi/poi-ooxml/4.1.2/poi-ooxml-4.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/poi/poi/4.1.2/poi-4.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/commons-codec/commons-codec/1.13/commons-codec-1.13.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-collections4/4.4/commons-collections4-4.4.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/zaxxer/SparseBitSet/1.2/SparseBitSet-1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/poi/poi-ooxml-schemas/4.1.2/poi-ooxml-schemas-4.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/xmlbeans/xmlbeans/3.1.0/xmlbeans-3.1.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-compress/1.19/commons-compress-1.19.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/virtuald/curvesapi/1.06/curvesapi-1.06.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/jsonwebtoken/jjwt/0.9.1/jjwt-0.9.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-data-redis/2.2.13.RELEASE/spring-boot-starter-data-redis-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-redis/2.2.12.RELEASE/spring-data-redis-2.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-keyvalue/2.2.12.RELEASE/spring-data-keyvalue-2.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-commons/2.2.12.RELEASE/spring-data-commons-2.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-tx/5.2.12.RELEASE/spring-tx-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-oxm/5.2.12.RELEASE/spring-oxm-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/lettuce/lettuce-core/5.2.2.RELEASE/lettuce-core-5.2.2.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-common/4.1.58.Final/netty-common-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-handler/4.1.58.Final/netty-handler-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-resolver/4.1.58.Final/netty-resolver-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-buffer/4.1.58.Final/netty-buffer-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-codec/4.1.58.Final/netty-codec-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-transport/4.1.58.Final/netty-transport-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/projectreactor/reactor-core/3.3.13.RELEASE/reactor-core-3.3.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/reactivestreams/reactive-streams/1.0.3/reactive-streams-1.0.3.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-pool2/2.7.0/commons-pool2-2.7.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/eu/bitwalker/UserAgentUtils/1.21/UserAgentUtils-1.21.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-boot-starter/3.3.0/mybatis-plus-boot-starter-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus/3.3.0/mybatis-plus-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-extension/3.3.0/mybatis-plus-extension-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-core/3.3.0/mybatis-plus-core-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-annotation/3.3.0/mybatis-plus-annotation-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-jdbc/2.2.13.RELEASE/spring-boot-starter-jdbc-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/zaxxer/HikariCP/3.4.5/HikariCP-3.4.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jdbc/5.2.12.RELEASE/spring-jdbc-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/mybatis-spring/2.0.0/mybatis-spring-2.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/cn/hutool/hutool-all/5.3.1/hutool-all-5.3.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/projectlombok/lombok/1.18.16/lombok-1.18.16.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/velocity/velocity/1.7/velocity-1.7.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/commons-lang/commons-lang/2.4/commons-lang-2.4.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/commons-collections/commons-collections/3.2.2/commons-collections-3.2.2.jar" enabled="true" runInBatchMode="false"/>
+</factorypath>

+ 113 - 0
mp-admin/pom.xml

@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>mp</artifactId>
+        <groupId>com.qs.mp</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+    <artifactId>mp-admin</artifactId>
+
+    <description>
+        web服务入口
+    </description>
+
+    <dependencies>
+
+        <!-- spring-boot-devtools -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <optional>true</optional> <!-- 表示依赖不会传递 -->
+        </dependency>
+
+        <!-- swagger3-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-boot-starter</artifactId>
+        </dependency>
+
+        <!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+            <version>1.6.2</version>
+        </dependency>
+
+         <!-- Mysql驱动包 -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <!-- 核心模块-->
+        <dependency>
+            <groupId>com.qs.mp</groupId>
+            <artifactId>mp-framework</artifactId>
+        </dependency>
+
+        <!-- 定时任务-->
+        <dependency>
+            <groupId>com.qs.mp</groupId>
+            <artifactId>mp-quartz</artifactId>
+        </dependency>
+
+        <!-- 代码生成-->
+        <dependency>
+            <groupId>com.qs.mp</groupId>
+            <artifactId>mp-generator</artifactId>
+        </dependency>
+        
+        <dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+			<exclusions>
+				<exclusion>
+					<groupId>com.vaadin.external.google</groupId>
+					<artifactId>android-json</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.skyscreamer</groupId>
+					<artifactId>jsonassert</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+        
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.1.1.RELEASE</version>
+                <configuration>
+                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>   
+                <groupId>org.apache.maven.plugins</groupId>   
+                <artifactId>maven-war-plugin</artifactId>   
+                <version>3.1.0</version>   
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                    <warName>${project.artifactId}</warName>
+                </configuration>   
+           </plugin>   
+        </plugins>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+
+</project>

+ 50 - 0
mp-admin/src/main/java/com/qs/mp/MpApplication.java

@@ -0,0 +1,50 @@
+package com.qs.mp;
+
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.converter.HttpMessageConverter;
+
+/**
+ * 启动程序
+ * 
+ * @author ygp
+ */
+@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
+public class MpApplication
+{
+    public static void main(String[] args)
+    {
+        // System.setProperty("spring.devtools.restart.enabled", "false");
+        SpringApplication.run(MpApplication.class, args);
+        System.out.println(" 盲票启动成功\n"
+        		+ "  / \\    / \\    | _ \\ \r\n"
+        		+ " /   \\  /   \\   |  _/ \r\n"
+        		+ "/     \\/     \\  |_|_  \r\n"
+        		+ "\"`-0-0-'\"`-0-0-'\"`-0-0-' ");
+    }
+
+
+	@Bean
+	public HttpMessageConverters fastJsonHttpMessageConverters() {
+		// 1.定义一个converters转换消息的对象
+		FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
+		// 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json数据
+		FastJsonConfig fastJsonConfig = new FastJsonConfig();
+		fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue,
+				SerializerFeature.WriteNullListAsEmpty,
+				SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullBooleanAsFalse,
+				SerializerFeature.PrettyFormat);
+		// 3.在converter中添加配置信息
+		fastConverter.setFastJsonConfig(fastJsonConfig);
+		// 4.将converter赋值给HttpMessageConverter
+		HttpMessageConverter<?> converter = fastConverter;
+		// 5.返回HttpMessageConverters对象
+		return new HttpMessageConverters(converter);
+	}
+}

+ 18 - 0
mp-admin/src/main/java/com/qs/mp/MpServletInitializer.java

@@ -0,0 +1,18 @@
+package com.qs.mp;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+/**
+ * web容器中进行部署
+ * 
+ * @author ygp
+ */
+public class MpServletInitializer extends SpringBootServletInitializer
+{
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
+    {
+        return application.sources(MpApplication.class);
+    }
+}

+ 58 - 0
mp-admin/src/main/java/com/qs/mp/handler/BodyReaderHttpServletRequestWrapper.java

@@ -0,0 +1,58 @@
+
+package com.qs.mp.handler;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
+
+	private final byte[] body;
+	
+	public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) {
+		super(request);
+		//  Auto-generated constructor stub
+		body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
+	}
+	
+	@Override
+    public BufferedReader getReader() throws IOException {
+        return new BufferedReader(new InputStreamReader(getInputStream()));
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+
+        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
+
+        return new ServletInputStream() {
+
+            @Override
+            public int read() throws IOException {
+                return bais.read();
+            }
+
+            @Override
+            public boolean isFinished() {
+                return false;
+            }
+
+            @Override
+            public boolean isReady() {
+                return false;
+            }
+
+            @Override
+            public void setReadListener(ReadListener readListener) {
+
+            }
+        };
+    }
+
+}

+ 132 - 0
mp-admin/src/main/java/com/qs/mp/handler/HttpHelper.java

@@ -0,0 +1,132 @@
+
+package com.qs.mp.handler;
+
+import com.alibaba.fastjson.JSONObject;
+import com.qs.mp.common.enums.HttpMethod;
+import com.qs.mp.common.utils.LogUtil;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HttpHelper {
+
+  protected static final Logger logger = LoggerFactory.getLogger(HttpHelper.class);
+
+  /**
+   * requestbody 解析
+   *
+   * @param request
+   * @return
+   */
+  public static String getBodyString(ServletRequest request) {
+    StringBuilder sb = new StringBuilder();
+    InputStream inputStream = null;
+    BufferedReader reader = null;
+    try {
+      inputStream = request.getInputStream();
+      reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
+      String line = "";
+      while ((line = reader.readLine()) != null) {
+        sb.append(line);
+      }
+    } catch (IOException e) {
+      LogUtil.error(logger,"Stream to String error", e);
+    } finally {
+      if (inputStream != null) {
+        try {
+          inputStream.close();
+        } catch (IOException e) {
+          LogUtil.error(logger,"Stream to String error", e);
+        }
+      }
+      if (reader != null) {
+        try {
+          reader.close();
+        } catch (IOException e) {
+          LogUtil.error(logger,"Stream to String error", e);
+        }
+      }
+    }
+    return sb.toString();
+  }
+
+  public static SortedMap<String, String> getAllParams(HttpServletRequest request)
+      throws IOException {
+
+    SortedMap<String, String> result = new TreeMap<>();
+    //获取URL上的参数
+    Map<String, String> urlParams = getUrlParams(request);
+    for (Map.Entry entry : urlParams.entrySet()) {
+      result.put((String) entry.getKey(), (String) entry.getValue());
+    }
+    Map<String, String> allRequestParam = new HashMap<>(16);
+    // get请求不需要拿body参数
+    if (!HttpMethod.GET.name().equals(request.getMethod())) {
+      allRequestParam = getAllRequestParam(request);
+    }
+    //将URL的参数和body参数进行合并
+    if (allRequestParam != null) {
+      for (Map.Entry entry : allRequestParam.entrySet()) {
+        result.put((String) entry.getKey(), (String) entry.getValue());
+      }
+    }
+    return result;
+  }
+
+  /**
+   * 获取 Body 参数
+   *
+   * @param request
+   * @author show
+   * @date 15:04 2019/5/30
+   */
+  public static Map<String, String> getAllRequestParam(final HttpServletRequest request)
+      throws IOException {
+
+    BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
+    String str = "";
+    StringBuilder wholeStr = new StringBuilder();
+    //一行一行的读取body体里面的内容;
+    while ((str = reader.readLine()) != null) {
+      wholeStr.append(str);
+    }
+    //转化成json对象
+    return JSONObject.parseObject(wholeStr.toString(), Map.class);
+  }
+
+  /**
+   * 将URL请求参数转换成Map
+   *
+   * @param request
+   * @author show
+   */
+  public static Map<String, String> getUrlParams(HttpServletRequest request) {
+
+    String param = "";
+    try {
+      param = URLDecoder.decode(request.getQueryString(), "utf-8");
+    } catch (UnsupportedEncodingException e) {
+      LogUtil.error(logger,"", e);
+    }
+    Map<String, String> result = new HashMap<>(16);
+    String[] params = param.split("&");
+    for (String s : params) {
+      int index = s.indexOf("=");
+      result.put(s.substring(0, index), s.substring(index + 1));
+    }
+    return result;
+  }
+
+}

+ 122 - 0
mp-admin/src/main/java/com/qs/mp/handler/auth/SignInterceptor.java

@@ -0,0 +1,122 @@
+package com.qs.mp.handler.auth;
+
+import com.alibaba.fastjson.JSONObject;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.common.utils.http.HttpHelper;
+import com.qs.mp.core.domain.LoginUser;
+import com.qs.mp.framework.security.handle.HostHolder;
+import com.qs.mp.handler.BodyReaderHttpServletRequestWrapper;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+/**
+ * @author daixiaodan
+ *
+ */
+
+@Component
+public class SignInterceptor implements HandlerInterceptor {
+
+    public final static String ACCESS_TOKEN = "token";
+    public final static String PARAM_SIGN = "sign";
+    public final static int EXPIRE_TIME = 1440 * 1000; // 24h
+    public final static int TOKEN_EXPIRE_CODE = 406 ;
+    protected Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private HostHolder hostHolder;
+
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        //logger.info("request URL:" + getRequestInfo(request));
+        if (!(handler instanceof HandlerMethod)) {
+            return true;
+        }
+        String method = request.getMethod();
+        if ("post".equalsIgnoreCase(method)) {
+            // 最后用RequestBody中获取token
+            ServletRequest tempRequest = null;
+            tempRequest = new BodyReaderHttpServletRequestWrapper(request);
+            String body = HttpHelper.getBodyString(tempRequest);
+            String timestamp = request.getHeader("x-zz-timestamp");
+            //进行sign验证
+            String[] signs = request.getParameterValues("sign");
+
+            if(null == signs || signs.length == 0) {
+                logger.error("sign参数为空 request url:"+getRequestInfo(request));
+                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                return false;
+            }
+            if(StringUtils.isBlank(body)){
+                logger.info("request body is null. request url:"+getRequestInfo(request));
+                //response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                // 上传文件的时候,body里只有文件流,custId在request参数里
+                String custId = request.getParameter("custId");
+                if(!StringUtils.isBlank(custId)){
+                    LoginUser loginUser = hostHolder.getUser();
+//                    loginUser.setCustId(custId);
+                    hostHolder.setUser(loginUser);
+                }
+                return true;
+            }
+
+            String sign = signs.length> 0 ? signs[0] : "";
+            boolean verify =SignUtils.verifySign(sign, body, timestamp);
+            long time = Long.valueOf(timestamp);
+            if(StringUtils.isBlank(timestamp)) {
+                logger.error("请求时间无效或者超时");
+                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                return false;
+            } else if(!verify) {
+                logger.error("sign参数无效");
+                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                return false;
+            } /*else if (System.currentTimeMillis() - time > EXPIRE_TIME) {
+                logger.error("request time expire... request URL:" + getRequestInfo
+                        (request)+" x-zz-timestamp:"+time);
+                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                return false;
+            }*/
+            //特殊处理,通过拦截器注入线程对象属性custId modify by duota 2021.09.09
+            try {
+                if (JSONObject.isValidObject(body)) {
+                    JSONObject jsonObject = JSONObject.parseObject(body);
+                    String custId = jsonObject.getString("custId");
+                    if (!StringUtils.isBlank(custId)) {
+                        LoginUser loginUser = hostHolder.getUser();
+//                        loginUser.setCustId(custId);
+                        hostHolder.setUser(loginUser);
+                    }
+                }
+            }catch (Exception e){
+                LogUtil.error(logger, e, "拦截器处理custId异常,body:{0}", body );
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
+        
+    }
+
+    private String getRequestInfo(HttpServletRequest request) {
+        return request.getRequestURL().toString() + " query param:" + request.getQueryString();
+    }
+
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
+
+    }
+
+}

+ 47 - 0
mp-admin/src/main/java/com/qs/mp/handler/auth/SignUtils.java

@@ -0,0 +1,47 @@
+package com.qs.mp.handler.auth;
+
+import com.qs.mp.common.utils.sign.Md5Utils;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+
+
+/**
+ * 签名工具类
+ * @author duota
+ *
+ */
+public class SignUtils {
+    
+    protected static Logger logger = LoggerFactory.getLogger(SignUtils.class);
+    @Value("${sign.md5key}")
+    private static String md5key = "3Jr8S1K18rcC1wAfv8";
+    public static String md5(String string) {
+        byte[] hash;
+        try {
+            hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("UTF-8 is unsupported", e);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("MessageDigest不支持MD5Util", e);
+        }
+        StringBuilder hex = new StringBuilder(hash.length * 2);
+        for (byte b : hash) {
+            if ((b & 0xFF) < 0x10)
+                hex.append("0");
+            hex.append(Integer.toHexString(b & 0xFF));
+        }
+        return hex.toString();
+    }
+    
+    public static boolean verifySign(String sign,String body,String timestamp) {
+        String l = body+ "nonce"+ timestamp + md5key;
+        //logger.info("sign str:"+l);
+        String mySign = Md5Utils.hash(l);
+        //logger.info("mySign:"+mySign+" sign:"+sign);
+        return mySign.equals(sign);
+    }
+}

+ 45 - 0
mp-admin/src/main/java/com/qs/mp/handler/auth/WebConfigurer.java

@@ -0,0 +1,45 @@
+package com.qs.mp.handler.auth;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Component
+public class WebConfigurer implements WebMvcConfigurer {
+
+    @Autowired
+    private SignInterceptor signInterceptor;
+
+    // 这个方法是用来配置静态资源的,比如html,js,css等
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+
+    }
+    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
+    // addPathPatterns("/**") 表示拦截所有的请求,// excludePathPatterns("/login",
+    // "/register") 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(signInterceptor).
+        addPathPatterns("/**")
+        .excludePathPatterns(
+            "/service/notify/wxcallback",
+            "/service/notify/payCallback",
+            "/captchaImage");
+    }
+
+    @Override
+    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+        Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
+        builder.indentOutput(true);
+        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
+
+    }
+}

+ 319 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/BaseController.java

@@ -0,0 +1,319 @@
+package com.qs.mp.web.controller;
+
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import com.qs.mp.common.constant.HttpStatus;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.PageDomain;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.core.page.TableSupport;
+import com.qs.mp.common.enums.ErrorCodeEnum;
+import com.qs.mp.common.utils.DateUtils;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.common.utils.sql.SqlUtil;
+import com.qs.mp.core.domain.LoginUser;
+import java.beans.PropertyEditorSupport;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+
+/**
+ * web层通用数据处理
+ * 
+ * @author ygp
+ */
+public class BaseController
+{
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * 将前台传递过来的日期格式的字符串,自动转化为Date类型
+     */
+    @InitBinder
+    public void initBinder(WebDataBinder binder)
+    {
+        // Date 类型转换
+        binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
+        {
+            @Override
+            public void setAsText(String text)
+            {
+                setValue(DateUtils.parseDate(text));
+            }
+        });
+    }
+
+    /**
+     * 设置请求分页数据
+     */
+    protected void startPage()
+    {
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+        if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
+        {
+            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
+            PageHelper.startPage(pageNum, pageSize, orderBy);
+        }
+    }
+
+    /**
+     * 设置请求排序数据
+     */
+    protected void startOrderBy()
+    {
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        if (StringUtils.isNotEmpty(pageDomain.getOrderBy()))
+        {
+            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
+            PageHelper.orderBy(orderBy);
+        }
+    }
+
+    /**
+     * 响应请求分页数据
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    protected TableDataInfo getDataTable(List<?> list)
+    {
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(new PageInfo(list).getTotal());
+        return rspData;
+    }
+
+    /**
+     * 返回成功
+     */
+    public AjaxResult success()
+    {
+        return AjaxResult.success();
+    }
+    
+    /**
+     * 返回成功消息
+     */
+    public AjaxResult success(String message)
+    {
+        return AjaxResult.success(message);
+    }
+    
+    /**
+     * 返回成功
+     */
+    public AjaxResult success(String msg,Object data)
+    {
+        return AjaxResult.success(msg, data);
+    }
+    
+    public AjaxResult success(Object data) {
+        return AjaxResult.success(data);
+    }
+
+    
+    /**
+     *登陆超时
+     */
+    public AjaxResult timeout()
+    {
+        return AjaxResult.timeout();
+    }
+    
+    
+    /**
+     *登陆超时
+     */
+    public AjaxResult timeout(String msg)
+    {
+        return AjaxResult.timeout(msg);
+    }
+
+
+    /**
+     * 返回失败消息
+     */
+    public AjaxResult error()
+    {
+        return AjaxResult.error();
+    }
+
+
+    
+    
+
+    /**
+     * 返回失败消息
+     */
+    public AjaxResult error(String message)
+    {
+        return AjaxResult.error(message);
+    }
+    
+    /**
+     * 返回成功
+     */
+    public AjaxResult error(String msg,Object data)
+    {
+        return AjaxResult.error(msg, data);
+    }
+
+    /**
+     * 返回错误码消息
+     */
+    public AjaxResult error(AjaxResult.Type type, String message)
+    {
+        return new AjaxResult(type, message);
+    }
+    
+    
+    public AjaxResult error(int code, String message)
+    {
+        return new AjaxResult(code, message,null);
+    }
+
+    public AjaxResult error(ErrorCodeEnum errorCodeEnum) {
+      return new AjaxResult(errorCodeEnum.getCode(), errorCodeEnum.getMsg(), null);
+    }
+
+    /**
+     * 响应返回结果
+     * 
+     * @param rows 影响行数
+     * @return 操作结果
+     */
+    protected AjaxResult toAjax(int rows)
+    {
+        return rows > 0 ? AjaxResult.success() : AjaxResult.error();
+    }
+    
+
+    /**
+     * 响应返回结果
+     * 
+     * @param rows 影响行数
+     * @return 操作结果
+     */
+    protected AjaxResult toAjax(int rows,String msg)
+    {
+        return rows > 0 ? success(msg) : error(msg);
+    }
+
+    /**
+     * 响应返回结果
+     * 
+     * @param rows 影响行数
+     * @return 操作结果
+     */
+    protected AjaxResult toAjax(int rows,String msg, Object data)
+    {
+        return rows > 0 ? success(msg,data) : error(msg,data);
+    }
+
+
+    /**
+     * 响应返回结果
+     * 
+     * @param result 结果
+     * @return 操作结果
+     */
+    protected AjaxResult toAjax(boolean result)
+    {
+        return result ? success() : error();
+    }
+    
+    /**
+     * 响应返回结果
+     * 
+     * @param result 结果
+     * @return 操作结果
+     */
+   protected AjaxResult toAjax(boolean result,String msg)
+    {
+        return result ? success(msg) : error(msg);
+    }
+   
+   /**
+    * 响应返回结果
+    * 
+    * @param result 结果
+    * @return 操作结果
+    */
+  protected AjaxResult toAjax(boolean result,String msg, Object data)
+   {
+       return result ? success(msg,data) : error(msg, data);
+   }
+
+
+
+    
+    /**
+     * 响应返回结果
+     * 
+     * @param
+     * @return 操作结果
+     */
+   protected AjaxResult toAjax(AjaxResult.Type type , String msg, Object data)
+    {
+	   return new AjaxResult(type, msg, data);
+    }
+
+    
+    public Map<String, Object> getReturnJson(Map<String, Object> rsMap,int code,String msg,Object obj) {
+ 		rsMap.put("code", code);
+ 		rsMap.put("msg", msg);
+ 		if(null != obj){
+ 		   rsMap.put("data", obj);
+ 		}
+ 		logger.info("result data:"+rsMap);
+ 		return rsMap;
+ 		// return JSONObject.fromObject(rsMap).toString();
+ 	}
+
+    /**
+     * 页面跳转
+     */
+    public String redirect(String url)
+    {
+        return StringUtils.format("redirect:{}", url);
+    }
+
+    /**
+     * 获取用户缓存信息
+     */
+    public LoginUser getLoginUser()
+    {
+        return SecurityUtils.getLoginUser();
+    }
+
+    /**
+     * 获取登录用户id
+     */
+    public Long getUserId()
+    {
+        return getLoginUser().getUserId();
+    }
+
+    /**
+     * 获取登录部门id
+     */
+    public Long getDeptId()
+    {
+        return getLoginUser().getDeptId();
+    }
+
+    /**
+     * 获取登录用户名
+     */
+    public String getUsername()
+    {
+        return getLoginUser().getUsername();
+    }
+}

+ 126 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/api/callback/PayCallBackController.java

@@ -0,0 +1,126 @@
+package com.qs.mp.web.controller.api.callback;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.common.utils.WebhookService;
+import com.qs.mp.pay.domain.PayOrder;
+import com.qs.mp.pay.service.IPayOrderService;
+import com.qs.mp.pay.service.IWalletService;
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @auther duota
+ * @create 2021 2021/9/6 2:54 下午
+ * @describe 三方支付平台回调
+ */
+
+@RestController
+@RequestMapping("/service/notify")
+public class PayCallBackController {
+
+  private final Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());
+  private String UNKNOWN = "UNKNOWN";
+
+  @Autowired
+  private IPayOrderService payOrderService;
+
+  @Autowired
+  private IWalletService walletService;
+
+  @RequestMapping(value = "/payCallback", method = RequestMethod.POST)
+  public void payCallback(HttpServletRequest request, HttpServletResponse response) {
+    // 获取请求的 IP 地址 增加三方服务器IP地址白名单
+    String ipAddress = getIpAddr(request);
+    logger.info("IP地址:" + ipAddress);
+    //IP地址白名单判断 121.196.180.137 三方支付服务器IP白名单
+    String orderNo =  request.getParameter("orderNo");
+    if(!"121.196.180.137".equalsIgnoreCase(ipAddress)){
+      //暂时增加机器人告警,防止三方服务器ip变更信息同步不到位,导致支付故障
+      WebhookService.sendMonitorData("【三方支付请求IP异常】{"+ipAddress+"} 订单号:"+orderNo);
+    }
+    String orderStatus =  request.getParameter("orderStatus");
+    String shopOrderNo =  request.getParameter("shopOrderNo");
+
+    String code =  request.getParameter("code");
+    String result =  request.getParameter("result");
+    String completeDate =  request.getParameter("completeDate");
+    String channelNo =  request.getParameter("channelNo");
+    String payType =  request.getParameter("payType");
+    if(StringUtils.isEmpty(orderNo) ||StringUtils.isEmpty(orderStatus) || StringUtils.isEmpty(shopOrderNo) || StringUtils.isEmpty(code) ){
+      LogUtil.error(logger,"参数异常");
+      responseWrite(response,"error");
+      return;
+    }
+    int payMoney = Integer.parseInt(request.getParameter("payMoney"));
+    logger.info("data: orderNo:"+orderNo +" orderStatus:"+orderStatus +" shopOrderNo:"+shopOrderNo+ "channelNo:"+channelNo+" payType:"+payType);
+
+    //回调金额与订单金额一致性校验
+    PayOrder order = payOrderService.getById(shopOrderNo);
+    if(order.getTransactionAmount() != payMoney){
+      //Todo 告警
+      logger.error("回调金额与订单金额不一致");
+      responseWrite(response,"error");
+      return;
+    }
+    PayOrder payOrder =  new PayOrder();
+    payOrder.setOrderId(shopOrderNo);
+    payOrder.setOrderNo(orderNo);
+    payOrder.setCompleteDate(completeDate);
+    payOrder.setChannelNo(channelNo);
+    payOrder.setOrderstatus(orderStatus);
+    // 订单支付状态单独保存
+    LambdaUpdateWrapper<PayOrder> updateWrapper = new LambdaUpdateWrapper<>();
+    updateWrapper.eq(PayOrder::getOrderstatus,2);
+    updateWrapper.eq(PayOrder::getOrderId,shopOrderNo);
+    boolean ret = payOrderService.update(payOrder,updateWrapper);
+    if(ret){
+      //Todo 更新账户余额,单独封装一个事物接口服务
+      logger.info("支付回调消息更新成功 shopOrderNo:"+shopOrderNo);
+      walletService.payOrderStatusHandle(shopOrderNo);
+      responseWrite(response,"success");
+    }else{
+      LogUtil.error(logger,"支付回调消息更新失败 shopOrderNo:"+shopOrderNo);
+      responseWrite(response,"error");
+    }
+  }
+
+  private void responseWrite(HttpServletResponse response,String echostr){
+    try {
+      response.getWriter().write(echostr);
+      response.getWriter().flush();
+      response.getWriter().close();
+    } catch (IOException e) {
+      LogUtil.error(logger, e, "微信公众号设置url回调处理异常");
+    }
+  }
+
+  /**
+   * 获取 IP地址
+   * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址
+   * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,
+   * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址
+   */
+  private String getIpAddr(HttpServletRequest request) {
+    String ip = request.getHeader("x-forwarded-for");
+    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
+      ip = request.getHeader("Proxy-Client-IP");
+    }
+    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
+      ip = request.getHeader("WL-Proxy-Client-IP");
+    }
+    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
+      ip = request.getRemoteAddr();
+    }
+    return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
+  }
+
+}

+ 133 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/api/callback/WxCallBackController.java

@@ -0,0 +1,133 @@
+package com.qs.mp.web.controller.api.callback;
+
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.common.utils.http.HttpHelper;
+import com.qs.mp.framework.domain.WxCallbackMsgBody;
+import com.qs.mp.framework.security.handle.HostHolder;
+import com.qs.mp.framework.service.IWxAccountService;
+import com.qs.mp.handler.BodyReaderHttpServletRequestWrapper;
+import com.qs.mp.web.controller.common.BaseApiController;
+import java.io.IOException;
+import java.util.Arrays;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * e签宝回调
+ *
+ * @author zhongcp
+ */
+@RestController
+@RequestMapping("/service/notify/*")
+public class WxCallBackController extends BaseApiController {
+
+  private final Logger logger = LoggerFactory.getLogger(WxCallBackController.class);
+
+  private final String TOKEN = "qXuGAggYMWk5HLXA";
+
+  @Autowired
+  private HostHolder hostHolder;
+
+  @Autowired
+  private IWxAccountService wxAccountService;
+
+  /**
+   * 微信公众号接口配置验证 特别注意,此处不能直接返回echostr,因为最后输出后会加上双引号,导致微信公众号设置页面提示token验证失败。 所以直接通过response写回去
+   *
+   * @return
+   */
+  @RequestMapping(value = "/wxcallback", method = RequestMethod.GET)
+  public void checkSignature(@RequestParam("signature") String signature,
+      @RequestParam("timestamp") String timestamp,
+      @RequestParam("nonce") String nonce, @RequestParam("echostr") String echostr,
+      HttpServletResponse response) {
+    LogUtil.info(logger, "signature:{0}, timestamp:{1}, nonce:{2}, echostr:{3}",
+        signature, timestamp, nonce, echostr);
+    // 第一步:自然排序
+    String[] tmp = {TOKEN, timestamp, nonce};
+    Arrays.sort(tmp);
+    // 第二步:sha1 加密
+    String sourceStr = StringUtils.join(tmp);
+    String localSignature = DigestUtils.sha1Hex(sourceStr);
+    LogUtil.info(logger, "localSignature:{0}", localSignature);
+    // 第三步:验证签名
+    if (signature.equals(localSignature)) {
+      LogUtil.info(logger, "签名验证通过,返回:{0}", echostr);
+      try {
+        response.getWriter().write(echostr);
+        response.getWriter().flush();
+        response.getWriter().close();
+      } catch (IOException e) {
+        LogUtil.error(logger, e, "微信公众号设置url回调处理异常");
+      }
+
+    }
+
+  }
+
+  @RequestMapping(value = "/wxcallback", method = RequestMethod.POST)
+  public void callback(HttpServletRequest request, HttpServletResponse response) {
+    ServletRequest tempRequest = new BodyReaderHttpServletRequestWrapper(request);
+    String body = HttpHelper.getBodyString(tempRequest);
+    String signature = request.getParameter("signature");
+    String timestamp = request.getParameter("timestamp");
+    String nonce = request.getParameter("nonce");
+    LogUtil.info(logger, "signature:{0}, timestamp:{1}, nonce:{2}",
+        signature, timestamp, nonce);
+    LogUtil.info(logger, "wx回调数据:{0}", JSON.toJSONString(body));
+
+
+
+
+    /*if (StringUtils.isEmpty(data)) {
+      LogUtil.error(logger, "wx回调通知请求参数为空");
+      return "{\"code\":101}";
+    }*/
+    try {
+      XmlMapper mapper = new XmlMapper();
+      WxCallbackMsgBody wxCallbackMsgBody = mapper.readValue(body, WxCallbackMsgBody.class);
+      LogUtil.info(logger, "wxCallbackMsgBody:{0}", JSON.toJSONString(wxCallbackMsgBody));
+      // 目前只处理关注/取关事件消息,其他忽略
+      boolean rst = false;
+      if ("event".equals(wxCallbackMsgBody.getMsgType())) {
+        if ("subscribe".equals(wxCallbackMsgBody.getEvent())) {
+          rst = wxAccountService.followGzh(wxCallbackMsgBody.getFromUserName());
+          LogUtil.info(logger, "关注公众号事件处理结果为{0},openid:{1}",
+              rst, wxCallbackMsgBody.getFromUserName());
+        } else if ("unsubscribe".equals(wxCallbackMsgBody.getEvent())) {
+          rst = wxAccountService.unFollowGzh(wxCallbackMsgBody.getFromUserName());
+          LogUtil.info(logger, "取消关注公众号事件处理结果为{0},openid:{1}",
+              rst, wxCallbackMsgBody.getFromUserName());
+        } else {
+          LogUtil.info(logger, "非关注/取关事件暂不做处理");
+        }
+      } else {
+        LogUtil.info(logger, "非事件类消息暂不做处理");
+      }
+    } catch (Exception e) {
+      LogUtil.error(logger, e, "wx回调通知处理异常", null);
+    }
+
+    try {
+      response.getWriter().write("success");
+      response.getWriter().flush();
+      response.getWriter().close();
+    } catch (IOException e) {
+      LogUtil.error(logger, e, "wx回调处理异常");
+    }
+
+  }
+
+}

+ 76 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/api/sms/SmsController.java

@@ -0,0 +1,76 @@
+package com.qs.mp.web.controller.api.sms;
+
+/**
+ *
+ * @describe 短信验证码API,支持登录、提现验证码发送,运营短信通知发送
+ * @auther duota
+ * @create 2021-09-3 16:17:48
+ */
+
+import cn.jsms.api.ValidSMSResult;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.jsms.JSMSUtils;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.sms.domain.SmsCode;
+import com.qs.mp.sms.service.ISmsLogService;
+import com.qs.mp.web.controller.common.BaseApiController;
+import io.swagger.annotations.Api;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@Api("短信验证码服务")
+@RestController
+@RequestMapping("/api/v1/ygp/*")
+@Component
+public class SmsController extends BaseApiController {
+
+  @Autowired
+  private ISmsLogService smsLogService;
+
+  @RequestMapping(value = "/sms/sendSmsCode", method = RequestMethod.POST)
+  @ResponseBody
+  public AjaxResult sendSmsCode(@RequestBody SmsCode smsCode) {
+    logger.info("sendSmsCode...mobile"+smsCode.getMobile() +" messageId"+smsCode.getMessageId()+" code:"+smsCode.getCode());
+    String msgId = smsLogService.sendSmsVerifyCode(smsCode.getMobile());
+    if (StringUtils.isNotBlank(msgId)) {
+      return success("发送成功.",msgId);
+    }
+    return error("验证码发送失败.");
+  }
+
+  @RequestMapping(value = "/sms/authsend", method = RequestMethod.POST)
+  @ResponseBody
+  public AjaxResult authsend(@RequestBody SmsCode smsCode) {
+    logger.info("sendSmsCode...mobile"+smsCode.getMobile() +" messageId"+smsCode.getMessageId()+" code:"+smsCode.getCode());
+    String msgId = smsLogService.sendSmsVerifyCode(smsCode.getMobile());
+    if (StringUtils.isNotBlank(msgId)) {
+      return success("发送成功.",msgId);
+    }
+    return error("验证码发送失败.");
+  }
+
+
+  @RequestMapping(value = "/sms/validSmsCode", method = RequestMethod.POST)
+  @ResponseBody
+  public AjaxResult validSmsCode(@RequestBody SmsCode smsCode) {
+
+    if(smsCode.getCode() == null || smsCode.getCode().length() != 6) {
+      return error("请输入6位验证码");
+    }
+
+    ValidSMSResult res = JSMSUtils.sendValidSMSCode(smsCode.getMessageId(), smsCode.getCode());
+    if (res != null) {
+      if (res.getIsValid()) {
+        return success(res.getIsValid());
+      }
+    }
+    logger.error("code:"+smsCode.getCode()+" mesageId:"+smsCode.getMessageId());
+    return error("验证码错误.");
+  }
+
+}

+ 255 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/api/user/UserController.java

@@ -0,0 +1,255 @@
+package com.qs.mp.web.controller.api.user;
+
+import cn.jsms.api.ValidSMSResult;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.enums.WxActTypeEnum;
+import com.qs.mp.common.jsms.JSMSUtils;
+import com.qs.mp.common.qcloud.QcloudFileUtils;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.common.utils.UUIDUtils;
+import com.qs.mp.common.utils.http.HttpUtils;
+import com.qs.mp.core.domain.LoginUser;
+import com.qs.mp.framework.domain.WxAccount;
+import com.qs.mp.framework.security.handle.HostHolder;
+import com.qs.mp.framework.service.IWxAccountService;
+import com.qs.mp.system.domain.SysUser;
+import com.qs.mp.system.domain.vo.WxLoginParams;
+import com.qs.mp.system.service.ISysUserService;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.common.BaseApiController;
+import io.swagger.annotations.Api;
+import java.io.File;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.io.FileUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @auther duota
+ * @create 2021 2021/9/6 6:16 下午
+ * @describe
+ */
+
+@Api("商户钱包API")
+@RestController
+@RequestMapping("/api/v1/ygp")
+@Component
+public class UserController extends BaseApiController {
+
+  @Autowired
+  private ISysUserService sysUserService;
+
+  @Autowired
+  protected HostHolder hostHolder;
+
+  @Autowired
+  private IWxAccountService wxAccountService;
+
+  @Value("${wx.appId}")
+  private String appId;
+  @Value("${wx.appSecret}")
+  private String appSecret;
+
+  /**
+   * 文件上传路径
+   */
+  @Value("${mp.profile}")
+  public String filePath;
+
+  /**
+   * 公开
+   */
+  @Value("${cloud.public-bucket-name}")
+  private String publicBucketName;
+
+
+  @RequestMapping(value = "/user/wxauth", method = RequestMethod.POST)
+  @ResponseBody
+  public AjaxResult wxauth(@RequestBody WxLoginParams wxLoginParams) {
+    //调用微信后台接口获取openId
+    String res = wxLoginCheck(wxLoginParams, appSecret);
+    JSONObject jsonObject = JSONObject.parseObject(res);
+    String openId = jsonObject.getString("openid");
+    String sessionKey = jsonObject.getString("session_key");
+    String unionId = jsonObject.getString("unionid");
+
+    if (StringUtils.isBlank(openId) || StringUtils.isBlank(sessionKey)) {
+      LogUtil.error(logger, "微信登录失败:" + jsonObject.toJSONString());
+      return error("登录失败");
+    }
+    //更新数据库用户授权信息
+    LoginUser loginUser = hostHolder.getUser();
+    SysUser user = new SysUser();
+    user.setUserId(loginUser.getUserId());
+    user.setOpenId(openId);
+    user.setSessionKey(sessionKey);
+    user.setUnionId(unionId);
+    user.setLoginDate(new Date());
+    //如果用户头像字段为空,则默认获取用户微信头像
+    String avatarUrl = wxLoginParams.getAvatarUrl();
+
+    //用户先关联过公众号,那么授权后进行自动绑定
+    LambdaQueryWrapper<WxAccount> wrapper = new LambdaQueryWrapper();
+    wrapper.eq(WxAccount::getUnionid,unionId);
+    wrapper.eq(WxAccount::getActType, WxActTypeEnum.GZH.getValue());
+    List<WxAccount> accountList = wxAccountService.list(wrapper);
+    if(accountList.size() == 1){
+      user.setGzhOpenId(accountList.get(0).getOpenid());
+    }
+    if(StringUtils.isBlank(loginUser.getUser().getAvatar()) && !StringUtils.isBlank(avatarUrl)){
+      try {
+        //下载并且重新上传头像
+        JSONObject result = saveHeadImage(avatarUrl,String.valueOf(user.getUserId()));
+        if(result != null){
+          user.setAvatar(result.getString("fileName"));
+        }
+      } catch (Exception e) {
+        LogUtil.error(logger,"头像上传异常",e.getMessage());
+      }
+    }
+    logger.info("update user data:"+JSONObject.toJSONString(user));
+    int rows = sysUserService.updateUserProfile(user);
+    if (rows != 1) {
+      LogUtil.error(logger,"用户微信授权异常");
+      return error("用户微信授权异常");
+    }
+    return success("微信授权成功");
+  }
+
+  /**
+   * 重置密码
+   */
+  @RequestMapping(value = "/user/resetPwd", method = RequestMethod.POST)
+  public AjaxResult resetPwd(@RequestBody JSONObject user) {
+    //验证短信验证码
+    ValidSMSResult res = JSMSUtils.sendValidSMSCode(
+        user.getString("messageId"), user.getString("code"));
+    if (res == null || !res.getIsValid()) {
+      return error("短信验证码错误");
+    }
+
+    int rows = sysUserService.resetUserPwd(user.getString("userName"),
+        SecurityUtils.encryptPassword(user.getString("password")));
+    if (rows == 1) {
+      return success("密码修改成功.");
+    } else {
+      return error("密码修改失败");
+    }
+  }
+
+  /**
+   * 查询当前登录用户信息
+   */
+  @RequestMapping(value = "/user/getLoginUserinfo", method = RequestMethod.POST)
+  public AjaxResult getLoginUserinfo(@RequestBody JSONObject params) {
+    LoginUser loginUser = hostHolder.getUser();
+    JSONObject jsonObject = new JSONObject();
+    jsonObject.put("userName", loginUser.getUsername());
+    jsonObject.put("avatar", loginUser.getUser().getAvatar());
+    jsonObject.put("nickName", loginUser.getUser().getNickName());
+    JSONArray roleKeys = new JSONArray();
+    JSONArray roles = new JSONArray();
+    loginUser.getUser().getRoles().forEach(sysRole -> roles.add(sysRole.getRoleName()));
+    jsonObject.put("roleKeys", roleKeys);
+    jsonObject.put("roles", roles);
+
+    return success(jsonObject);
+  }
+
+
+
+  /**
+   * code 换取 session_key
+   *
+   * @param
+   * @return
+   */
+  private String wxLoginCheck(WxLoginParams params, String appSecret) {
+    String param = "appid=" + appId + "&secret=" + appSecret + "&js_code=" + params.getCode()
+        + "&grant_type=authorization_code";
+    String res = HttpUtils.sendGet("https://api.weixin.qq.com/sns/jscode2session", param);
+    return res;
+  }
+
+  /**
+   * { "subscribe": 1, "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", "nickname": "Band", "sex": 1,
+   * "language": "zh_CN", "city": "广州", "province": "广东", "country": "中国", "headimgurl":
+   * "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
+   * "subscribe_time": 1382694957, "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL" }
+   *
+   * @param accessToken
+   * @param openId
+   * @return
+   */
+  private String getWxUserInfo(String accessToken, String openId) {
+    String url = "https://api.weixin.qq.com/cgi-bin/user/info";
+    String params = "access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
+    String res = HttpUtils.sendGet(url, params);
+    logger.info("weixin user info :" + res);
+    return res;
+  }
+
+  /**
+   * {"access_token":"ACCESS_TOKEN","expires_in":7200}
+   *
+   * @param appId
+   * @param appSecret
+   * @return
+   */
+  private String wxGetAccessToken(String appId, String appSecret) {
+    String param = "appid=" + appId + "&secret=" + appSecret + "&grant_type=client_credential";
+    String res = HttpUtils.sendGet("https://api.weixin.qq.com/cgi-bin/token", param);
+    JSONObject tokenJson = JSONObject.parseObject(res);
+    String token = tokenJson.getString("access_token");
+    return token;
+
+  }
+
+  private JSONObject saveHeadImage(String serverUrl,String userId) throws Exception {
+    String outFilePath = filePath+"/headImage";
+    File outPath = new File(outFilePath);
+    if (!outPath.exists()) {
+      if (!outPath.mkdirs()) {
+        throw new Exception("makdirs: '" + outFilePath + "'fail");
+      }
+    }
+    URL url = new URL(serverUrl);
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    conn.setConnectTimeout(3 * 1000);
+    //防止屏蔽程序抓取而放回403错误
+    conn.setRequestProperty("User-Agent", "Mozilla/4.0(compatible;MSIE 5.0;Windows NT;DigExt)");
+    Long totalSize = Long.parseLong(conn.getHeaderField("Content-Length"));
+    String name = userId+"/"+ UUIDUtils.newId();
+    String mimeType = "image/jpg";
+    //logger.info("图片开始下载. totalSize" + totalSize);
+    if (totalSize > 0) {
+      File file = new File(outFilePath+"/"+userId);
+      FileUtils.copyURLToFile(url, file);
+      //logger.info("图片下载完成.");
+      QcloudFileUtils.putFile(file, name, publicBucketName, mimeType );
+      FileUtils.delete(file);
+    } else {
+      throw new Exception("can not find serverUrl :{}" + serverUrl);
+    }
+    JSONObject result = new JSONObject();
+    result.put("fileName",name);
+    result.put("fileType",mimeType);
+    return result;
+  }
+
+}

+ 44 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/common/BaseApiController.java

@@ -0,0 +1,44 @@
+package com.qs.mp.web.controller.common;
+
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+
+/**
+ * 
+ * 开放API基类
+ * 
+ * @author duota
+ *
+ */
+public class BaseApiController extends BaseController {
+
+	  /**
+     * 全部数据权限
+     */
+    public static final String DATA_SCOPE_ALL = "1";
+
+    /**
+     * 自定数据权限
+     */
+    public static final String DATA_SCOPE_CUSTOM = "2";
+
+    /**
+     * 数据权限过滤关键字
+     */
+    public static final String DATA_SCOPE = "dataScope";
+    
+    public static String listToString(List<String> list) {
+        StringBuilder sb = new StringBuilder();
+        if (list != null && list.size() > 0) {
+            for (int i = 0; i < list.size(); i++) {
+                if (i < list.size() - 1) {
+                    sb.append("'"+list.get(i) + "',");
+                } else {
+                    sb.append("'"+list.get(i)+"'");
+                }
+            }
+        }
+        return sb.toString();
+    }
+}
+

+ 96 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/common/CaptchaController.java

@@ -0,0 +1,96 @@
+package com.qs.mp.web.controller.common;
+
+import com.google.code.kaptcha.Producer;
+import com.qs.mp.common.constant.Constants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.redis.RedisCache;
+import com.qs.mp.common.utils.sign.Base64;
+import com.qs.mp.common.utils.uuid.IdUtils;
+import com.qs.mp.system.service.ISysConfigService;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.util.FastByteArrayOutputStream;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 验证码操作处理
+ * 
+ * @author ygp
+ */
+@RestController
+public class CaptchaController
+{
+    @Resource(name = "captchaProducer")
+    private Producer captchaProducer;
+
+    @Resource(name = "captchaProducerMath")
+    private Producer captchaProducerMath;
+
+    @Autowired
+    private RedisCache redisCache;
+    
+    // 验证码类型
+    @Value("${mp.captchaType}")
+    private String captchaType;
+    
+    @Autowired
+    private ISysConfigService configService;
+    /**
+     * 生成验证码
+     */
+    @GetMapping("/captchaImage")
+    public AjaxResult getCode(HttpServletResponse response) throws IOException
+    {
+        AjaxResult ajax = AjaxResult.success();
+        boolean captchaOnOff = configService.selectCaptchaOnOff();
+        ajax.put("captchaOnOff", captchaOnOff);
+        if (!captchaOnOff)
+        {
+            return ajax;
+        }
+
+        // 保存验证码信息
+        String uuid = IdUtils.simpleUUID();
+        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
+
+        String capStr = null, code = null;
+        BufferedImage image = null;
+
+        // 生成验证码
+        if ("math".equals(captchaType))
+        {
+            String capText = captchaProducerMath.createText();
+            capStr = capText.substring(0, capText.lastIndexOf("@"));
+            code = capText.substring(capText.lastIndexOf("@") + 1);
+            image = captchaProducerMath.createImage(capStr);
+        }
+        else if ("char".equals(captchaType))
+        {
+            capStr = code = captchaProducer.createText();
+            image = captchaProducer.createImage(capStr);
+        }
+
+        redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
+        // 转换流信息写出
+        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
+        try
+        {
+            ImageIO.write(image, "jpg", os);
+        }
+        catch (IOException e)
+        {
+            return AjaxResult.error(e.getMessage());
+        }
+
+        ajax.put("uuid", uuid);
+        ajax.put("img", Base64.encode(os.toByteArray()));
+        return ajax;
+    }
+}

+ 118 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/common/CommonController.java

@@ -0,0 +1,118 @@
+package com.qs.mp.web.controller.common;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import com.qs.mp.common.config.MpConfig;
+import com.qs.mp.common.constant.Constants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.common.utils.file.FileUploadUtils;
+import com.qs.mp.common.utils.file.FileUtils;
+import com.qs.mp.framework.config.ServerConfig;
+
+/**
+ * 通用请求处理
+ * 
+ * @author ygp
+ */
+@RestController
+public class CommonController
+{
+    private static final Logger log = LoggerFactory.getLogger(CommonController.class);
+
+    @Autowired
+    private ServerConfig serverConfig;
+
+    /**
+     * 通用下载请求
+     * 
+     * @param fileName 文件名称
+     * @param delete 是否删除
+     */
+    @GetMapping("common/download")
+    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
+    {
+        try
+        {
+            if (!FileUtils.checkAllowDownload(fileName))
+            {
+                throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
+            }
+            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
+            String filePath = MpConfig.getDownloadPath() + fileName;
+
+            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+            FileUtils.setAttachmentResponseHeader(response, realFileName);
+            FileUtils.writeBytes(filePath, response.getOutputStream());
+            if (delete)
+            {
+                FileUtils.deleteFile(filePath);
+            }
+        }
+        catch (Exception e)
+        {
+            log.error("下载文件失败", e);
+        }
+    }
+
+    /**
+     * 通用上传请求
+     */
+    @PostMapping("/common/upload")
+    public AjaxResult uploadFile(MultipartFile file) throws Exception
+    {
+        try
+        {
+            // 上传文件路径
+            String filePath = MpConfig.getUploadPath();
+            // 上传并返回新文件名称
+            String fileName = FileUploadUtils.upload(filePath, file);
+            String url = serverConfig.getUrl() + fileName;
+            AjaxResult ajax = AjaxResult.success();
+            ajax.put("fileName", fileName);
+            ajax.put("url", url);
+            return ajax;
+        }
+        catch (Exception e)
+        {
+            return AjaxResult.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 本地资源通用下载
+     */
+    @GetMapping("/common/download/resource")
+    public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
+            throws Exception
+    {
+        try
+        {
+            if (!FileUtils.checkAllowDownload(resource))
+            {
+                throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
+            }
+            // 本地资源路径
+            String localPath = MpConfig.getProfile();
+            // 数据库资源地址
+            String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
+            // 下载名称
+            String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
+            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+            FileUtils.setAttachmentResponseHeader(response, downloadName);
+            FileUtils.writeBytes(downloadPath, response.getOutputStream());
+        }
+        catch (Exception e)
+        {
+            log.error("下载文件失败", e);
+        }
+    }
+}

+ 94 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/common/FileDownloadController.java

@@ -0,0 +1,94 @@
+package com.qs.mp.web.controller.common;
+
+import com.qs.mp.common.config.MpConfig;
+import com.qs.mp.common.qcloud.QcloudFileUtils;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.common.utils.file.FileUtils;
+import com.qs.mp.core.domain.LoginUser;
+import com.qs.mp.framework.security.handle.HostHolder;
+import java.io.File;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequestMapping("/api/v1/qs/*")
+@RestController
+@Component
+public class FileDownloadController extends BaseApiController {
+
+  private static final Logger log = LoggerFactory.getLogger(FileDownloadController.class);
+
+  /**
+   * 文件上传路径
+   */
+  @Value("${mp.profile}")
+  public String filePath;
+
+
+  /**
+   * 私有
+   */
+  @Value("${cloud.private-bucket-name}")
+  private String privateBucketName;
+
+  @Autowired
+  protected HostHolder hostHolder;
+
+  @GetMapping("file/remote/download")
+  public void fileDownload(@RequestParam("name") String fileName, HttpServletResponse response,
+      HttpServletRequest request) {
+    if (StringUtils.isBlank(fileName)) {
+      LogUtil.error(logger, "下载文件参数为空");
+      return;
+    }
+
+    LoginUser loginUser = hostHolder.getUser();
+//    String custId = loginUser.getCustId();
+
+    // 判断获取权限,文件名上带了custid的,只有匹配当前登录的custId才能下载
+    // 目前是get请求,商户端登录后custid没有存到loginUser里,导致这里获取不到,故先不做校验,
+    // 后续要做校验再考虑商户端登录后把custid存到loginUser里。
+    /*if (fileName.indexOf("/") > 0) {
+      if (fileName.indexOf(custId) < 0) {
+        LogUtil.error(logger, "文件和当前用户的客户ID不匹配,fileName:{0}, custId:{1}",
+            fileName, custId);
+        return;
+      }
+    }*/
+
+    int idx = fileName.lastIndexOf("/");
+    String outFileDir = MpConfig.getDownloadPath() + (idx > 0 ? fileName.substring(0, idx + 1) : "");
+    File tempFile = new File(outFileDir);
+    if(!tempFile.exists()) {
+      tempFile.mkdirs();
+    }
+    try {
+
+      QcloudFileUtils.downloadFile(fileName, privateBucketName, MpConfig.getDownloadPath(),
+          fileName);
+
+      String realFileName =
+          System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
+      String filePath = MpConfig.getDownloadPath() + fileName;
+
+      response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+      FileUtils.setAttachmentResponseHeader(response, realFileName);
+      FileUtils.writeBytes(filePath, response.getOutputStream());
+
+      FileUtils.deleteFile(filePath);
+
+    } catch (Exception e) {
+      LogUtil.error(logger, e, "下载文件失败,fileName:{0}, custId:{1}", fileName, null);
+    }
+  }
+}

+ 199 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/common/FileUploadController.java

@@ -0,0 +1,199 @@
+package com.qs.mp.web.controller.common;
+
+
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.qcloud.QcloudFileUtils;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.common.utils.UUIDUtils;
+import com.qs.mp.core.domain.LoginUser;
+import com.qs.mp.core.domain.UploadAttachment;
+import com.qs.mp.framework.security.handle.HostHolder;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+import net.coobird.thumbnailator.Thumbnails;
+import net.coobird.thumbnailator.Thumbnails.Builder;
+import net.coobird.thumbnailator.geometry.Positions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+@RequestMapping("/api/v1/ygp/*")
+@RestController
+@Component
+public class FileUploadController extends BaseApiController {
+    private static final Logger log = LoggerFactory.getLogger(FileUploadController.class);
+
+    /**
+     * 文件上传路径
+     */
+    @Value("${mp.profile}")
+    public String filePath;
+
+    /**
+     * 公开
+     */
+    @Value("${cloud.public-bucket-name}")
+    private String publicBucketName;
+
+    /**
+     * 私有
+     */
+    @Value("${cloud.private-bucket-name}")
+    private String privateBucketName;
+
+    /**
+     * 模板原稿文件目录
+     */
+    @Value("${cloud.template-dir}")
+    private String tempSrcDir;
+
+    @Autowired
+    protected HostHolder hostHolder;
+
+    /**
+     * 文件上传
+     * @param fileType 文件类型
+     * @param file 文件对象
+     * @return
+     */
+    @PutMapping("file/remote/upload/{fileType}")
+    public AjaxResult uploadFile(@PathVariable("fileType") String fileType, @RequestParam("file") MultipartFile file) {
+        try {
+            if(null ==  file) {
+                return AjaxResult.error("上传文件参数为空.");
+            }
+            //logger.info("upload...");
+            LoginUser loginUser = hostHolder.getUser();
+//            String custId = loginUser.getCustId();
+            String mimeType = file.getContentType();
+            // 上传文件路径
+            LogUtil.info(log, "文件上传,fileName:{0}, ContentType:{1}", new Object[]{file.getOriginalFilename(), mimeType});
+            String fileName = file.getOriginalFilename();
+            String saveAs = fileName;
+            // 模板原稿文件保存到指定目录
+            if ("template".equals(fileType)){
+                saveAs = tempSrcDir + fileName;
+            }
+            QcloudFileUtils.putStream(file.getInputStream(), saveAs,
+                privateBucketName, mimeType);
+            UploadAttachment uploadFile = new UploadAttachment(saveAs, mimeType);
+            return success("", uploadFile);
+        } catch (Exception e) {
+            LogUtil.error(log, e, "文件上传失败.");
+            return AjaxResult.error("文件上传失败.");
+        }
+    }
+
+    /**
+     * 上传图片,并生成缩略图
+     *
+     * @param auth 是否公开可访问,0 可访问,1 不可访问
+     * @param file 图片文件
+     * @return
+     */
+    @PostMapping("image/remote/upload/post/{auth}")
+    public AjaxResult postImage(@PathVariable("auth") int auth, @RequestParam("file") MultipartFile file)  {
+        return uploadImageFile(auth, file);
+    }
+
+    private AjaxResult uploadImageFile(
+        @PathVariable("auth") int auth,
+        @RequestParam("file") MultipartFile file) {
+        try {
+            if (null == file) {
+                return AjaxResult.error("上传文件参数为空.");
+            }
+            //logger.info("upload...");
+            LoginUser loginUser = hostHolder.getUser();
+            String bucketName = privateBucketName;
+            if (0 == auth) {
+                bucketName = publicBucketName;
+            }
+            // 上传文件路径
+            LogUtil.info(logger, "ContentType:" + file.getContentType());
+            String mimeType = file.getContentType();
+            if (StringUtils.isBlank(mimeType)) {
+                mimeType = "image/jpg";
+            }
+            LogUtil.info(logger," mimeType:" + mimeType + " fileName:" + file.getOriginalFilename());
+            String name = commpressPicAndUpLoadOSS(file, mimeType, 480, 480, bucketName);
+            if (StringUtils.isBlank(name)) {
+                LogUtil.error(logger, "图片上传失败.");
+                return AjaxResult.error("图片上传失败.");
+            }
+            return success("", new UploadAttachment(name, mimeType));
+        } catch (Exception e) {
+            LogUtil.error(log, e, "文件上传失败.");
+            return AjaxResult.error("文件上传失败.");
+        }
+    }
+
+    /**
+     * 上传图片,并生成缩略图
+     *
+     * @param auth 是否公开可访问,0 可访问,1 不可访问
+     * @param file 图片文件
+     * @return
+     */
+    @PutMapping("image/remote/upload/{auth}")
+    public AjaxResult uploadImage(@PathVariable("auth") int auth, @RequestParam("file") MultipartFile file)  {
+        return uploadImageFile(auth, file);
+    }
+
+
+
+    private String commpressPicAndUpLoadOSS(MultipartFile file,String mimeType,int width,int height,String bucketName) {
+        String fileName = file.getOriginalFilename();
+        int idx = fileName.lastIndexOf('.');
+        String suffix = "";
+        if (idx > 0) {
+            suffix = fileName.substring(idx);
+        }
+        String name = UUIDUtils.newId() + suffix;
+        try {
+            QcloudFileUtils.putStream(file.getInputStream(), name, bucketName, mimeType );
+            BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
+            Builder<BufferedImage> builder = Thumbnails.of(bufferedImage);
+            int pWidth = bufferedImage.getWidth();
+            int pHeight = bufferedImage.getHeight();
+            if(pWidth > pHeight && pWidth > width) {
+                builder.height(height);
+            }else if(pHeight > pWidth && pHeight > height){
+                builder.width(480);
+            } else {
+                builder.size(pWidth, pHeight);
+            }
+            String outFileDir = filePath+"/thumb";
+            File tempFile = new File(outFileDir);
+            if(!tempFile.exists()) {
+                tempFile.mkdirs();
+            }
+            String thumbName = name +"_s";
+            String outFilePath = filePath+"/thumb/"+thumbName+"."+mimeType.substring(mimeType.lastIndexOf("/")+1);
+            builder.toFile(outFilePath);
+            //裁剪尺寸
+            Thumbnails.of(outFilePath).scale(1f).sourceRegion(Positions.CENTER, 480,480).toFile(outFilePath);
+            File outFile = new File(outFilePath);
+            QcloudFileUtils.putFile(outFile, thumbName, bucketName, mimeType);
+            if(outFile.exists()) {
+                LogUtil.info(log, "delete file..."+outFilePath);
+                outFile.delete();
+            }
+        }  catch (Exception e) {
+            LogUtil.error(log, e, "");
+            return null;
+        }
+        return name;
+    }
+}

+ 53 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/monitor/CacheController.java

@@ -0,0 +1,53 @@
+package com.qs.mp.web.controller.monitor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.utils.StringUtils;
+
+/**
+ * 缓存监控
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/monitor/cache")
+public class CacheController
+{
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+    @GetMapping()
+    public AjaxResult getInfo() throws Exception
+    {
+        Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
+        Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
+        Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
+
+        Map<String, Object> result = new HashMap<>(3);
+        result.put("info", info);
+        result.put("dbSize", dbSize);
+
+        List<Map<String, String>> pieList = new ArrayList<>();
+        commandStats.stringPropertyNames().forEach(key -> {
+            Map<String, String> data = new HashMap<>(2);
+            String property = commandStats.getProperty(key);
+            data.put("name", StringUtils.removeStart(key, "cmdstat_"));
+            data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
+            pieList.add(data);
+        });
+        result.put("commandStats", pieList);
+        return AjaxResult.success(result);
+    }
+}

+ 27 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/monitor/ServerController.java

@@ -0,0 +1,27 @@
+package com.qs.mp.web.controller.monitor;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.framework.web.domain.Server;
+
+/**
+ * 服务器监控
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/monitor/server")
+public class ServerController
+{
+    @PreAuthorize("@ss.hasPermi('monitor:server:list')")
+    @GetMapping()
+    public AjaxResult getInfo() throws Exception
+    {
+        Server server = new Server();
+        server.copyTo();
+        return AjaxResult.success(server);
+    }
+}

+ 67 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/monitor/SysLogininforController.java

@@ -0,0 +1,67 @@
+package com.qs.mp.web.controller.monitor;
+
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.system.domain.SysLogininfor;
+import com.qs.mp.system.service.ISysLogininforService;
+
+/**
+ * 系统访问记录
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/monitor/logininfor")
+public class SysLogininforController extends BaseController
+{
+    @Autowired
+    private ISysLogininforService logininforService;
+
+    @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysLogininfor logininfor)
+    {
+        startPage();
+        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
+        return getDataTable(list);
+    }
+
+    @Log(title = "登录日志", businessType = BusinessType.EXPORT)
+    @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
+    @GetMapping("/export")
+    public AjaxResult export(SysLogininfor logininfor)
+    {
+        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
+        ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
+        return util.exportExcel(list, "登录日志", true);
+    }
+
+    @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
+    @Log(title = "登录日志", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{infoIds}")
+    public AjaxResult remove(@PathVariable Long[] infoIds)
+    {
+        return toAjax(logininforService.deleteLogininforByIds(infoIds));
+    }
+
+    @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
+    @Log(title = "登录日志", businessType = BusinessType.CLEAN)
+    @DeleteMapping("/clean")
+    public AjaxResult clean()
+    {
+        logininforService.cleanLogininfor();
+        return AjaxResult.success();
+    }
+}

+ 67 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/monitor/SysOperlogController.java

@@ -0,0 +1,67 @@
+package com.qs.mp.web.controller.monitor;
+
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.system.domain.SysOperLog;
+import com.qs.mp.system.service.ISysOperLogService;
+
+/**
+ * 操作日志记录
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/monitor/operlog")
+public class SysOperlogController extends BaseController
+{
+    @Autowired
+    private ISysOperLogService operLogService;
+
+    @PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysOperLog operLog)
+    {
+        startPage();
+        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
+        return getDataTable(list);
+    }
+
+    @Log(title = "操作日志", businessType = BusinessType.EXPORT)
+    @PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
+    @GetMapping("/export")
+    public AjaxResult export(SysOperLog operLog)
+    {
+        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
+        ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
+        return util.exportExcel(list, "操作日志", true);
+    }
+
+    @Log(title = "操作日志", businessType = BusinessType.DELETE)
+    @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
+    @DeleteMapping("/{operIds}")
+    public AjaxResult remove(@PathVariable Long[] operIds)
+    {
+        return toAjax(operLogService.deleteOperLogByIds(operIds));
+    }
+
+    @Log(title = "操作日志", businessType = BusinessType.CLEAN)
+    @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
+    @DeleteMapping("/clean")
+    public AjaxResult clean()
+    {
+        operLogService.cleanOperLog();
+        return AjaxResult.success();
+    }
+}

+ 92 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/monitor/SysUserOnlineController.java

@@ -0,0 +1,92 @@
+package com.qs.mp.web.controller.monitor;
+
+import com.qs.mp.core.domain.LoginUser;
+import com.qs.mp.web.controller.BaseController;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.constant.Constants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.core.redis.RedisCache;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.system.domain.SysUserOnline;
+import com.qs.mp.system.service.ISysUserOnlineService;
+
+/**
+ * 在线用户监控
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/monitor/online")
+public class SysUserOnlineController extends BaseController
+{
+    @Autowired
+    private ISysUserOnlineService userOnlineService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @PreAuthorize("@ss.hasPermi('monitor:online:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(String ipaddr, String userName)
+    {
+        Collection<String> keys = redisCache.keys(Constants.LOGIN_TOKEN_KEY + "*");
+        List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
+        for (String key : keys)
+        {
+            LoginUser user = redisCache.getCacheObject(key);
+            if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
+            {
+                if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
+                {
+                    userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
+                }
+            }
+            else if (StringUtils.isNotEmpty(ipaddr))
+            {
+                if (StringUtils.equals(ipaddr, user.getIpaddr()))
+                {
+                    userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
+                }
+            }
+            else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
+            {
+                if (StringUtils.equals(userName, user.getUsername()))
+                {
+                    userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
+                }
+            }
+            else
+            {
+                userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
+            }
+        }
+        Collections.reverse(userOnlineList);
+        userOnlineList.removeAll(Collections.singleton(null));
+        return getDataTable(userOnlineList);
+    }
+
+    /**
+     * 强退用户
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
+    @Log(title = "在线用户", businessType = BusinessType.FORCE)
+    @DeleteMapping("/{tokenId}")
+    public AjaxResult forceLogout(@PathVariable String tokenId)
+    {
+        redisCache.deleteObject(Constants.LOGIN_TOKEN_KEY + tokenId);
+        return AjaxResult.success();
+    }
+}

+ 167 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/quartz/SysJobController.java

@@ -0,0 +1,167 @@
+package com.qs.mp.web.controller.quartz;
+
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.constant.Constants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.common.exception.job.TaskException;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.quartz.domain.SysJob;
+import com.qs.mp.quartz.service.ISysJobService;
+import com.qs.mp.quartz.util.CronUtils;
+
+/**
+ * 调度任务信息操作处理
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/monitor/job")
+public class SysJobController extends BaseController
+{
+    @Autowired
+    private ISysJobService jobService;
+
+    /**
+     * 查询定时任务列表
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysJob sysJob)
+    {
+        startPage();
+        List<SysJob> list = jobService.selectJobList(sysJob);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出定时任务列表
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
+    @Log(title = "定时任务", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(SysJob sysJob)
+    {
+        List<SysJob> list = jobService.selectJobList(sysJob);
+        ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
+        return util.exportExcel(list, "定时任务", true);
+    }
+
+    /**
+     * 获取定时任务详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+    @GetMapping(value = "/{jobId}")
+    public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
+    {
+        return AjaxResult.success(jobService.selectJobById(jobId));
+    }
+
+    /**
+     * 新增定时任务
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:add')")
+    @Log(title = "定时任务", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException
+    {
+        if (!CronUtils.isValid(job.getCronExpression()))
+        {
+            return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
+        }
+        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
+        {
+            return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
+        }
+        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP))
+        {
+            return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap://'调用");
+        }
+        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
+        {
+            return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
+        }
+        job.setCreateBy(getUsername());
+        return toAjax(jobService.insertJob(job));
+    }
+
+    /**
+     * 修改定时任务
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
+    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException
+    {
+        if (!CronUtils.isValid(job.getCronExpression()))
+        {
+            return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确");
+        }
+        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
+        {
+            return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
+        }
+        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP))
+        {
+            return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap://'调用");
+        }
+        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
+        {
+            return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
+        }
+        job.setUpdateBy(getUsername());
+        return toAjax(jobService.updateJob(job));
+    }
+
+    /**
+     * 定时任务状态修改
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
+    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
+    {
+        SysJob newJob = jobService.selectJobById(job.getJobId());
+        newJob.setStatus(job.getStatus());
+        return toAjax(jobService.changeStatus(newJob));
+    }
+
+    /**
+     * 定时任务立即执行一次
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
+    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+    @PutMapping("/run")
+    public AjaxResult run(@RequestBody SysJob job) throws SchedulerException
+    {
+        jobService.run(job);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 删除定时任务
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+    @Log(title = "定时任务", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{jobIds}")
+    public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
+    {
+        jobService.deleteJobByIds(jobIds);
+        return AjaxResult.success();
+    }
+}

+ 90 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/quartz/SysJobLogController.java

@@ -0,0 +1,90 @@
+package com.qs.mp.web.controller.quartz;
+
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.quartz.domain.SysJobLog;
+import com.qs.mp.quartz.service.ISysJobLogService;
+
+/**
+ * 调度日志操作处理
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/monitor/jobLog")
+public class SysJobLogController extends BaseController
+{
+    @Autowired
+    private ISysJobLogService jobLogService;
+
+    /**
+     * 查询定时任务调度日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysJobLog sysJobLog)
+    {
+        startPage();
+        List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出定时任务调度日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
+    @Log(title = "任务调度日志", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(SysJobLog sysJobLog)
+    {
+        List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+        ExcelUtil<SysJobLog> util = new ExcelUtil<SysJobLog>(SysJobLog.class);
+        return util.exportExcel(list, "调度日志", true);
+    }
+    
+    /**
+     * 根据调度编号获取详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+    @GetMapping(value = "/{configId}")
+    public AjaxResult getInfo(@PathVariable Long jobLogId)
+    {
+        return AjaxResult.success(jobLogService.selectJobLogById(jobLogId));
+    }
+
+
+    /**
+     * 删除定时任务调度日志
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+    @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{jobLogIds}")
+    public AjaxResult remove(@PathVariable Long[] jobLogIds)
+    {
+        return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
+    }
+
+    /**
+     * 清空定时任务调度日志
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+    @Log(title = "调度日志", businessType = BusinessType.CLEAN)
+    @DeleteMapping("/clean")
+    public AjaxResult clean()
+    {
+        jobLogService.cleanJobLog();
+        return AjaxResult.success();
+    }
+}

+ 137 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysConfigController.java

@@ -0,0 +1,137 @@
+package com.qs.mp.web.controller.system;
+
+
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.annotation.RepeatSubmit;
+import com.qs.mp.common.constant.UserConstants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.system.domain.SysConfig;
+import com.qs.mp.system.service.ISysConfigService;
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 参数配置 信息操作处理
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/config")
+public class SysConfigController extends BaseController
+{
+    @Autowired
+    private ISysConfigService configService;
+
+    /**
+     * 获取参数配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:config:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysConfig config)
+    {
+        startPage();
+        List<SysConfig> list = configService.selectConfigList(config);
+        return getDataTable(list);
+    }
+
+    @Log(title = "参数管理", businessType = BusinessType.EXPORT)
+    @PreAuthorize("@ss.hasPermi('system:config:export')")
+    @GetMapping("/export")
+    public AjaxResult export(SysConfig config)
+    {
+        List<SysConfig> list = configService.selectConfigList(config);
+        ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
+        return util.exportExcel(list, "参数数据",true);
+    }
+
+    /**
+     * 根据参数编号获取详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:config:query')")
+    @GetMapping(value = "/{configId}")
+    public AjaxResult getInfo(@PathVariable Long configId)
+    {
+        return AjaxResult.success(configService.selectConfigById(configId));
+    }
+
+    /**
+     * 根据参数键名查询参数值
+     */
+    @GetMapping(value = "/configKey/{configKey}")
+    public AjaxResult getConfigKey(@PathVariable String configKey)
+    {
+        return AjaxResult.success(configService.selectConfigByKey(configKey));
+    }
+
+    /**
+     * 新增参数配置
+     */
+    @PreAuthorize("@ss.hasPermi('system:config:add')")
+    @Log(title = "参数管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    @RepeatSubmit
+    public AjaxResult add(@Validated @RequestBody SysConfig config)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config)))
+        {
+            return AjaxResult.error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
+        }
+        config.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(configService.insertConfig(config));
+    }
+
+    /**
+     * 修改参数配置
+     */
+    @PreAuthorize("@ss.hasPermi('system:config:edit')")
+    @Log(title = "参数管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody SysConfig config)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config)))
+        {
+            return AjaxResult.error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
+        }
+        config.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(configService.updateConfig(config));
+    }
+
+    /**
+     * 删除参数配置
+     */
+    @PreAuthorize("@ss.hasPermi('system:config:remove')")
+    @Log(title = "参数管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{configIds}")
+    public AjaxResult remove(@PathVariable Long[] configIds)
+    {
+        configService.deleteConfigByIds(configIds);
+        return success();
+    }
+
+    /**
+     * 刷新参数缓存
+     */
+    @PreAuthorize("@ss.hasPermi('system:config:remove')")
+    @Log(title = "参数管理", businessType = BusinessType.CLEAN)
+    @DeleteMapping("/refreshCache")
+    public AjaxResult refreshCache()
+    {
+        configService.resetConfigCache();
+        return AjaxResult.success();
+    }
+}

+ 165 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysDeptController.java

@@ -0,0 +1,165 @@
+package com.qs.mp.web.controller.system;
+
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.constant.UserConstants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.system.domain.SysDept;
+import com.qs.mp.system.service.ISysDeptService;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 部门信息
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/dept")
+public class SysDeptController extends BaseController
+{
+    @Autowired
+    private ISysDeptService deptService;
+
+    /**
+     * 获取部门列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:dept:list')")
+    @GetMapping("/list")
+    public AjaxResult list(SysDept dept)
+    {
+        List<SysDept> depts = deptService.selectDeptList(dept);
+        return AjaxResult.success(depts);
+    }
+
+    /**
+     * 查询部门列表(排除节点)
+     */
+    @PreAuthorize("@ss.hasPermi('system:dept:list')")
+    @GetMapping("/list/exclude/{deptId}")
+    public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId)
+    {
+    	
+        List<SysDept> depts = deptService.selectDeptList(new SysDept());
+        Iterator<SysDept> it = depts.iterator();
+        while (it.hasNext())
+        {
+            SysDept d = (SysDept) it.next();
+            if (d.getDeptId().intValue() == deptId
+                    || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""))
+            {
+                it.remove();
+            }
+        }
+        return AjaxResult.success(depts);
+    }
+
+    /**
+     * 根据部门编号获取详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:dept:query')")
+    @GetMapping(value = "/{deptId}")
+    public AjaxResult getInfo(@PathVariable Long deptId)
+    {
+        deptService.checkDeptDataScope(deptId);
+        return AjaxResult.success(deptService.selectDeptById(deptId));
+    }
+
+    /**
+     * 获取部门下拉树列表
+     */
+    @GetMapping("/treeselect")
+    public AjaxResult treeselect(SysDept dept)
+    {
+        List<SysDept> depts = deptService.selectDeptList(dept);
+        return AjaxResult.success(deptService.buildDeptTreeSelect(depts));
+    }
+
+    /**
+     * 加载对应角色部门列表树
+     */
+    @GetMapping(value = "/roleDeptTreeselect/{roleId}")
+    public AjaxResult roleDeptTreeselect(@PathVariable("roleId") Long roleId)
+    {
+        List<SysDept> depts = deptService.selectDeptList(new SysDept());
+        AjaxResult ajax = AjaxResult.success();
+        ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
+        ajax.put("depts", deptService.buildDeptTreeSelect(depts));
+        return ajax;
+    }
+
+    /**
+     * 新增部门
+     */
+    @PreAuthorize("@ss.hasPermi('system:dept:add')")
+    @Log(title = "部门管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody SysDept dept)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
+        {
+            return AjaxResult.error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
+        }
+        dept.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(deptService.insertDept(dept));
+    }
+
+    /**
+     * 修改部门
+     */
+    @PreAuthorize("@ss.hasPermi('system:dept:edit')")
+    @Log(title = "部门管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody SysDept dept)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
+        {
+            return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
+        }
+        else if (dept.getParentId().equals(dept.getDeptId()))
+        {
+            return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
+        }
+        else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
+                && deptService.selectNormalChildrenDeptById(dept.getDeptId()) > 0)
+        {
+            return AjaxResult.error("该部门包含未停用的子部门!");
+        }
+        dept.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(deptService.updateDept(dept));
+    }
+
+    /**
+     * 删除部门
+     */
+    @PreAuthorize("@ss.hasPermi('system:dept:remove')")
+    @Log(title = "部门管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{deptId}")
+    public AjaxResult remove(@PathVariable Long deptId)
+    {
+        if (deptService.hasChildByDeptId(deptId))
+        {
+            return AjaxResult.error("存在下级部门,不允许删除");
+        }
+        if (deptService.checkDeptExistUser(deptId))
+        {
+            return AjaxResult.error("部门存在用户,不允许删除");
+        }
+        return toAjax(deptService.deleteDeptById(deptId));
+    }
+}

+ 122 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysDictDataController.java

@@ -0,0 +1,122 @@
+package com.qs.mp.web.controller.system;
+
+
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.system.domain.SysDictData;
+import com.qs.mp.system.service.ISysDictDataService;
+import com.qs.mp.system.service.ISysDictTypeService;
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 数据字典信息
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/dict/data")
+public class SysDictDataController extends BaseController
+{
+    @Autowired
+    private ISysDictDataService dictDataService;
+
+    @Autowired
+    private ISysDictTypeService dictTypeService;
+
+    @PreAuthorize("@ss.hasPermi('system:dict:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysDictData dictData)
+    {
+        startPage();
+        List<SysDictData> list = dictDataService.selectDictDataList(dictData);
+        return getDataTable(list);
+    }
+
+    @Log(title = "字典数据", businessType = BusinessType.EXPORT)
+    @PreAuthorize("@ss.hasPermi('system:dict:export')")
+    @GetMapping("/export")
+    public AjaxResult export(SysDictData dictData)
+    {
+        List<SysDictData> list = dictDataService.selectDictDataList(dictData);
+        ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
+        return util.exportExcel(list, "字典数据",true);
+    }
+
+    /**
+     * 查询字典数据详细
+     */
+    @PreAuthorize("@ss.hasPermi('system:dict:query')")
+    @GetMapping(value = "/{dictCode}")
+    public AjaxResult getInfo(@PathVariable Long dictCode)
+    {
+        return AjaxResult.success(dictDataService.selectDictDataById(dictCode));
+    }
+
+    /**
+     * 根据字典类型查询字典数据信息
+     */
+    @GetMapping(value = "/type/{dictType}")
+    public AjaxResult dictType(@PathVariable String dictType)
+    {
+        List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
+        if (StringUtils.isNull(data))
+        {
+            data = new ArrayList<SysDictData>();
+        }
+        return AjaxResult.success(data);
+    }
+
+    /**
+     * 新增字典类型
+     */
+    @PreAuthorize("@ss.hasPermi('system:dict:add')")
+    @Log(title = "字典数据", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody SysDictData dict)
+    {
+        dict.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(dictDataService.insertDictData(dict));
+    }
+
+    /**
+     * 修改保存字典类型
+     */
+    @PreAuthorize("@ss.hasPermi('system:dict:edit')")
+    @Log(title = "字典数据", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody SysDictData dict)
+    {
+        dict.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(dictDataService.updateDictData(dict));
+    }
+
+    /**
+     * 删除字典类型
+     */
+    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
+    @Log(title = "字典类型", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{dictCodes}")
+    public AjaxResult remove(@PathVariable Long[] dictCodes)
+    {
+        dictDataService.deleteDictDataByIds(dictCodes);
+        return success();
+    }
+}

+ 132 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysDictTypeController.java

@@ -0,0 +1,132 @@
+package com.qs.mp.web.controller.system;
+
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.constant.UserConstants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.system.domain.SysDictType;
+import com.qs.mp.system.service.ISysDictTypeService;
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 数据字典信息
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/dict/type")
+public class SysDictTypeController extends BaseController
+{
+    @Autowired
+    private ISysDictTypeService dictTypeService;
+
+    @PreAuthorize("@ss.hasPermi('system:dict:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysDictType dictType)
+    {
+        startPage();
+        List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
+        return getDataTable(list);
+    }
+
+    @Log(title = "字典类型", businessType = BusinessType.EXPORT)
+    @PreAuthorize("@ss.hasPermi('system:dict:export')")
+    @GetMapping("/export")
+    public AjaxResult export(SysDictType dictType)
+    {
+        List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
+        ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
+        return util.exportExcel(list, "字典类型",true);
+    }
+
+    /**
+     * 查询字典类型详细
+     */
+    @PreAuthorize("@ss.hasPermi('system:dict:query')")
+    @GetMapping(value = "/{dictId}")
+    public AjaxResult getInfo(@PathVariable Long dictId)
+    {
+        return AjaxResult.success(dictTypeService.selectDictTypeById(dictId));
+    }
+
+    /**
+     * 新增字典类型
+     */
+    @PreAuthorize("@ss.hasPermi('system:dict:add')")
+    @Log(title = "字典类型", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody SysDictType dict)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict)))
+        {
+            return AjaxResult.error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
+        }
+        dict.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(dictTypeService.insertDictType(dict));
+    }
+
+    /**
+     * 修改字典类型
+     */
+    @PreAuthorize("@ss.hasPermi('system:dict:edit')")
+    @Log(title = "字典类型", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody SysDictType dict)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict)))
+        {
+            return AjaxResult.error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
+        }
+        dict.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(dictTypeService.updateDictType(dict));
+    }
+
+    /**
+     * 删除字典类型
+     */
+    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
+    @Log(title = "字典类型", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{dictIds}")
+    public AjaxResult remove(@PathVariable Long[] dictIds)
+    {
+        dictTypeService.deleteDictTypeByIds(dictIds);
+        return success();
+    }
+
+    /**
+     * 刷新字典缓存
+     */
+    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
+    @Log(title = "字典类型", businessType = BusinessType.CLEAN)
+    @DeleteMapping("/refreshCache")
+    public AjaxResult refreshCache()
+    {
+        dictTypeService.resetDictCache();
+        return AjaxResult.success();
+    }
+
+    /**
+     * 获取字典选择框列表
+     */
+    @GetMapping("/optionselect")
+    public AjaxResult optionselect()
+    {
+        List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
+        return AjaxResult.success(dictTypes);
+    }
+}

+ 29 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysIndexController.java

@@ -0,0 +1,29 @@
+package com.qs.mp.web.controller.system;
+
+import com.qs.mp.common.config.MpConfig;
+import com.qs.mp.common.utils.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 首页
+ *
+ * @author ygp
+ */
+@RestController
+public class SysIndexController
+{
+    /** 系统基础配置 */
+    @Autowired
+    private MpConfig mpConfig;
+
+    /**
+     * 访问首页,提示语
+     */
+    @RequestMapping("/")
+    public String index()
+    {
+        return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", mpConfig.getName(), mpConfig.getVersion());
+    }
+}

+ 125 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysLoginController.java

@@ -0,0 +1,125 @@
+package com.qs.mp.web.controller.system;
+
+
+import com.qs.mp.common.constant.Constants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.domain.model.LoginBody;
+import com.qs.mp.framework.security.handle.HostHolder;
+import com.qs.mp.framework.web.service.SysLoginService;
+import com.qs.mp.framework.web.service.SysPermissionService;
+import com.qs.mp.sms.domain.SmsCode;
+import com.qs.mp.system.domain.SysMenu;
+import com.qs.mp.system.domain.SysUser;
+import com.qs.mp.system.service.ISysMenuService;
+import com.qs.mp.utils.SecurityUtils;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 登录验证
+ * 
+ * @author ygp
+ */
+@RestController
+public class SysLoginController
+{
+    @Autowired
+    private SysLoginService loginService;
+
+    @Autowired
+    private ISysMenuService menuService;
+
+    @Autowired
+    private SysPermissionService permissionService;
+
+    @Autowired
+    private HostHolder hostHolder;
+
+    /**
+     * 登录方法
+     * 
+     * @param loginBody 登录信息
+     * @return 结果
+     */
+    @PostMapping("/login")
+    public AjaxResult login(@RequestBody LoginBody loginBody)
+    {
+        AjaxResult ajax = AjaxResult.success();
+        //校验验证码
+        loginService.validateCaptcha(loginBody.getUsername(), loginBody.getCode(),
+            loginBody.getUuid());
+
+        // 生成令牌
+        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
+                loginBody.getUuid(), loginBody.getIdentity());
+        ajax.put(Constants.TOKEN, token);
+        return ajax;
+    }
+
+    @PostMapping("/wxlogin")
+    public AjaxResult wxlogin(@RequestBody LoginBody loginBody)
+    {
+        AjaxResult ajax = AjaxResult.success();
+        // 生成令牌
+        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
+            loginBody.getUuid(), loginBody.getIdentity());
+        ajax.put(Constants.TOKEN, token);
+        return ajax;
+    }
+
+    @PostMapping("/auth/mobile")
+    public AjaxResult login(@RequestBody SmsCode smsCode)
+    {
+        Pattern codePattern = Pattern.compile("^[0-9]{6}");
+        if (!codePattern.matcher(smsCode.getCode()).matches()) {
+            return AjaxResult.error("请输入6位验证码");
+        }
+        AjaxResult ajax = AjaxResult.success();
+        String token = loginService.login(smsCode);
+        ajax.put(Constants.TOKEN, token);
+        return ajax;
+    }
+
+    /**
+     * 获取用户信息
+     * 
+     * @return 用户信息
+     */
+    @GetMapping("getInfo")
+    public AjaxResult getInfo()
+    {
+        SysUser user = SecurityUtils.getLoginUser().getUser();
+        // 角色集合
+        Set<String> roles = permissionService.getRolePermission(user);
+        // 权限集合
+        Set<String> permissions = permissionService.getMenuPermission(user);
+        AjaxResult ajax = AjaxResult.success();
+        ajax.put("user", user);
+        ajax.put("roles", roles);
+        ajax.put("permissions", permissions);
+        return ajax;
+    }
+
+    /**
+     * 获取路由信息
+     * 
+     * @return 路由信息
+     */
+    @GetMapping("getRouters")
+    public AjaxResult getRouters()
+    {
+        Long userId = SecurityUtils.getUserId();
+        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
+        List<SysMenu> permMenus = menuService.getChildPerms(menus, 0);
+        return AjaxResult.success(menuService.buildMenus(permMenus));
+    }
+}

+ 143 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysMenuController.java

@@ -0,0 +1,143 @@
+package com.qs.mp.web.controller.system;
+
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.constant.UserConstants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.system.domain.SysMenu;
+import com.qs.mp.system.service.ISysMenuService;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 菜单信息
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/menu")
+public class SysMenuController extends BaseController
+{
+    @Autowired
+    private ISysMenuService menuService;
+
+    /**
+     * 获取菜单列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:menu:list')")
+    @GetMapping("/list")
+    public AjaxResult list(SysMenu menu)
+    {
+        List<SysMenu> menus = menuService.selectMenuList(menu, SecurityUtils.getUserId());
+        return AjaxResult.success(menus);
+    }
+
+    /**
+     * 根据菜单编号获取详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:menu:query')")
+    @GetMapping(value = "/{menuId}")
+    public AjaxResult getInfo(@PathVariable Long menuId)
+    {
+        return AjaxResult.success(menuService.selectMenuById(menuId));
+    }
+
+    /**
+     * 获取菜单下拉树列表
+     */
+    @GetMapping("/treeselect")
+    public AjaxResult treeselect(SysMenu menu)
+    {
+        List<SysMenu> menus = menuService.selectMenuList(menu, SecurityUtils.getUserId());
+        return AjaxResult.success(menuService.buildMenuTreeSelect(menus));
+    }
+
+    /**
+     * 加载对应角色菜单列表树
+     */
+    @GetMapping(value = "/roleMenuTreeselect/{roleId}")
+    public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId)
+    {
+        List<SysMenu> menus = menuService.selectMenuList(SecurityUtils.getUserId());
+        AjaxResult ajax = AjaxResult.success();
+        ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
+        ajax.put("menus", menuService.buildMenuTreeSelect(menus));
+        return ajax;
+    }
+
+    /**
+     * 新增菜单
+     */
+    @PreAuthorize("@ss.hasPermi('system:menu:add')")
+    @Log(title = "菜单管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody SysMenu menu)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
+        {
+            return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
+        }
+        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
+        {
+            return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
+        }
+        menu.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(menuService.insertMenu(menu));
+    }
+
+    /**
+     * 修改菜单
+     */
+    @PreAuthorize("@ss.hasPermi('system:menu:edit')")
+    @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody SysMenu menu)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
+        {
+            return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
+        }
+        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
+        {
+            return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
+        }
+        else if (menu.getMenuId().equals(menu.getParentId()))
+        {
+            return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
+        }
+        menu.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(menuService.updateMenu(menu));
+    }
+
+    /**
+     * 删除菜单
+     */
+    @PreAuthorize("@ss.hasPermi('system:menu:remove')")
+    @Log(title = "菜单管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{menuId}")
+    public AjaxResult remove(@PathVariable("menuId") Long menuId)
+    {
+        if (menuService.hasChildByMenuId(menuId))
+        {
+            return AjaxResult.error("存在子菜单,不允许删除");
+        }
+        if (menuService.checkMenuExistRole(menuId))
+        {
+            return AjaxResult.error("菜单已分配,不允许删除");
+        }
+        return toAjax(menuService.deleteMenuById(menuId));
+    }
+}

+ 92 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysNoticeController.java

@@ -0,0 +1,92 @@
+package com.qs.mp.web.controller.system;
+
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.system.domain.SysNotice;
+import com.qs.mp.system.service.ISysNoticeService;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 公告 信息操作处理
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/notice")
+public class SysNoticeController extends BaseController
+{
+    @Autowired
+    private ISysNoticeService noticeService;
+
+    /**
+     * 获取通知公告列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:notice:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysNotice notice)
+    {
+        startPage();
+        List<SysNotice> list = noticeService.selectNoticeList(notice);
+        return getDataTable(list);
+    }
+
+    /**
+     * 根据通知公告编号获取详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:notice:query')")
+    @GetMapping(value = "/{noticeId}")
+    public AjaxResult getInfo(@PathVariable Long noticeId)
+    {
+        return AjaxResult.success(noticeService.selectNoticeById(noticeId));
+    }
+
+    /**
+     * 新增通知公告
+     */
+    @PreAuthorize("@ss.hasPermi('system:notice:add')")
+    @Log(title = "通知公告", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody SysNotice notice)
+    {
+        notice.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(noticeService.insertNotice(notice));
+    }
+
+    /**
+     * 修改通知公告
+     */
+    @PreAuthorize("@ss.hasPermi('system:notice:edit')")
+    @Log(title = "通知公告", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody SysNotice notice)
+    {
+        notice.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(noticeService.updateNotice(notice));
+    }
+
+    /**
+     * 删除通知公告
+     */
+    @PreAuthorize("@ss.hasPermi('system:notice:remove')")
+    @Log(title = "通知公告", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{noticeIds}")
+    public AjaxResult remove(@PathVariable Long[] noticeIds)
+    {
+        return toAjax(noticeService.deleteNoticeByIds(noticeIds));
+    }
+}

+ 130 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysPostController.java

@@ -0,0 +1,130 @@
+package com.qs.mp.web.controller.system;
+
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.constant.UserConstants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.system.domain.SysPost;
+import com.qs.mp.system.service.ISysPostService;
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 岗位信息操作处理
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/post")
+public class SysPostController extends BaseController
+{
+    @Autowired
+    private ISysPostService postService;
+
+    /**
+     * 获取岗位列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:post:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysPost post)
+    {
+        startPage();
+        List<SysPost> list = postService.selectPostList(post);
+        return getDataTable(list);
+    }
+    
+    @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
+    @PreAuthorize("@ss.hasPermi('system:post:export')")
+    @GetMapping("/export")
+    public AjaxResult export(SysPost post)
+    {
+        List<SysPost> list = postService.selectPostList(post);
+        ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
+        return util.exportExcel(list, "岗位数据",true);
+    }
+
+    /**
+     * 根据岗位编号获取详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:post:query')")
+    @GetMapping(value = "/{postId}")
+    public AjaxResult getInfo(@PathVariable Long postId)
+    {
+        return AjaxResult.success(postService.selectPostById(postId));
+    }
+
+    /**
+     * 新增岗位
+     */
+    @PreAuthorize("@ss.hasPermi('system:post:add')")
+    @Log(title = "岗位管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody SysPost post)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post)))
+        {
+            return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
+        }
+        else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post)))
+        {
+            return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
+        }
+        post.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(postService.insertPost(post));
+    }
+
+    /**
+     * 修改岗位
+     */
+    @PreAuthorize("@ss.hasPermi('system:post:edit')")
+    @Log(title = "岗位管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody SysPost post)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post)))
+        {
+            return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
+        }
+        else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post)))
+        {
+            return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
+        }
+        post.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(postService.updatePost(post));
+    }
+
+    /**
+     * 删除岗位
+     */
+    @PreAuthorize("@ss.hasPermi('system:post:remove')")
+    @Log(title = "岗位管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{postIds}")
+    public AjaxResult remove(@PathVariable Long[] postIds)
+    {
+        return toAjax(postService.deletePostByIds(postIds));
+    }
+
+    /**
+     * 获取岗位选择框列表
+     */
+    @GetMapping("/optionselect")
+    public AjaxResult optionselect()
+    {
+        List<SysPost> posts = postService.selectPostAll();
+        return AjaxResult.success(posts);
+    }
+}

+ 300 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysProfileController.java

@@ -0,0 +1,300 @@
+package com.qs.mp.web.controller.system;
+
+import cn.jsms.api.ValidSMSResult;
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.constant.UserConstants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.common.enums.ErrorCodeEnum;
+import com.qs.mp.common.exception.user.CaptchaExpireException;
+import com.qs.mp.common.jsms.JSMSUtils;
+import com.qs.mp.common.qcloud.QcloudFileUtils;
+import com.qs.mp.common.utils.LogUtil;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.common.utils.UUIDUtils;
+import com.qs.mp.core.domain.LoginUser;
+import com.qs.mp.framework.security.handle.HostHolder;
+import com.qs.mp.framework.web.service.TokenService;
+import com.qs.mp.system.domain.SysUser;
+import com.qs.mp.system.service.ISysUserService;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import com.qs.mp.web.controller.common.FileUploadController;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+import net.coobird.thumbnailator.Thumbnails;
+import net.coobird.thumbnailator.Thumbnails.Builder;
+import net.coobird.thumbnailator.geometry.Positions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 个人信息 业务处理
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/user/profile")
+public class SysProfileController extends BaseController
+{
+	
+	private static final Logger log = LoggerFactory.getLogger(FileUploadController.class);
+	
+    @Autowired
+    private ISysUserService userService;
+
+    @Autowired
+    private TokenService tokenService;
+    
+    @Autowired
+    protected HostHolder hostHolder;
+    
+    /**
+     * 文件上传路径
+     */
+    @Value("${mp.profile}")
+    public String filePath;
+    
+    /**
+     * 公开
+     */
+    @Value("${cloud.public-bucket-name}")
+    private String publicBucketName;
+    
+    /**
+     * 模板原稿文件目录
+     */
+    @Value("${cloud.template-dir}")
+    private String tempSrcDir;
+
+    /**
+     * 个人信息
+     */
+    @GetMapping
+    public AjaxResult profile()
+    {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        SysUser user = loginUser.getUser();
+        AjaxResult ajax = AjaxResult.success(user);
+        ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
+        ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
+        return ajax;
+    }
+
+    /**
+     * 修改用户
+     */
+    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult updateProfile(@RequestBody SysUser user)
+    {
+        if (StringUtils.isNotEmpty(user.getPhonenumber())
+                && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
+        {
+            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
+        }
+        if (StringUtils.isNotEmpty(user.getEmail())
+                && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
+        {
+            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
+        }
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        SysUser sysUser = loginUser.getUser();
+        user.setUserId(sysUser.getUserId());
+        user.setPassword(null);
+        if (userService.updateUserProfile(user) > 0)
+        {
+            // 更新缓存用户信息
+            sysUser.setNickName(user.getNickName());
+            sysUser.setPhonenumber(user.getPhonenumber());
+            sysUser.setEmail(user.getEmail());
+            sysUser.setSex(user.getSex());
+            tokenService.setLoginUser(loginUser);
+            return AjaxResult.success();
+        }
+        return AjaxResult.error("修改个人信息异常,请联系管理员");
+    }
+
+    /**
+     * 重置密码
+     */
+    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+    @PutMapping("/updatePwd")
+    public AjaxResult updatePwd(String oldPassword, String newPassword)
+    {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        String userName = loginUser.getUsername();
+        String password = loginUser.getPassword();
+        
+    
+        if (!SecurityUtils.matchesPassword(oldPassword, password))
+        {
+            return AjaxResult.error("修改密码失败,旧密码错误");
+        }
+        if (SecurityUtils.matchesPassword(newPassword, password))
+        {
+            return AjaxResult.error("新密码不能与旧密码相同");
+        }
+        if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0)
+        {
+            // 更新缓存用户密码
+            loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
+            tokenService.setLoginUser(loginUser);
+            return AjaxResult.success();
+        }
+        return AjaxResult.error("修改密码异常,请联系管理员");
+    }
+    
+    /**
+     * 重置密码
+     */
+    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+    @PutMapping("/resetPwd")
+    public AjaxResult resetPwd(String messageId, String authCode, String newPassword)
+    {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        String userName = loginUser.getUsername();
+        String password = loginUser.getPassword();
+        
+        //校验短信验证码
+        ValidSMSResult res = JSMSUtils.sendValidSMSCode(
+        		messageId, authCode);
+
+        if(res == null || !res.getIsValid()){
+            throw new CaptchaExpireException();
+        }
+        
+        if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0)
+        {
+            // 更新缓存用户密码
+            loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
+            tokenService.setLoginUser(loginUser);
+            return AjaxResult.success();
+        }
+        return AjaxResult.error("修改密码异常,请联系管理员");
+    }
+
+    /**
+     * 头像上传
+     */
+    @Log(title = "用户头像", businessType = BusinessType.UPDATE)
+    @PostMapping("/avatar")
+    public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws IOException
+    {
+        if (!file.isEmpty())
+        {
+            // LoginUser loginUser = SecurityUtils.getLoginUser();
+//            String avatar = FileUploadUtils.upload(YgpConfig.getAvatarPath(), file);
+//            if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
+//            {
+//                AjaxResult ajax = AjaxResult.success();
+//                ajax.put("imgUrl", avatar);
+//                // 更新缓存用户头像
+//                loginUser.getUser().setAvatar(avatar);
+//                tokenService.setLoginUser(loginUser);
+//                return ajax;
+//            }
+        	return uploadAvatar(file);
+        }
+        return AjaxResult.error("上传图片异常,请联系管理员");
+    }
+    
+    
+    private AjaxResult uploadAvatar(MultipartFile file) {
+        try {
+            if (null == file) {
+                return AjaxResult.error("上传文件参数为空.");
+            }
+            //logger.info("upload...");
+            LoginUser loginUser = hostHolder.getUser();
+            String userId = loginUser.getUserId()+"";
+            if (StringUtils.isBlank(userId)){
+                return error(ErrorCodeEnum.ERROR_CODE_1002);
+            }
+            String bucketName = publicBucketName;
+            // 上传文件路径
+            LogUtil.info(logger, "ContentType:" + file.getContentType());
+            String mimeType = file.getContentType();
+            if (StringUtils.isBlank(mimeType)) {
+                mimeType = "image/jpg";
+            }
+            LogUtil.info(logger,
+                "userId:" + userId + " mimeType:" + mimeType + " fileName:" + file.getOriginalFilename());
+            String name = commpressPicAndUpLoadOSS(userId, file, mimeType, 480, 480, bucketName);
+            if (StringUtils.isBlank(name)) {
+                LogUtil.error(logger, "头像上传失败.");
+                return AjaxResult.error("头像上传失败.");
+            }
+            
+            if (userService.updateUserAvatar(loginUser.getUsername(), name))
+            {
+                AjaxResult ajax = AjaxResult.success();
+                ajax.put("imgUrl", name);
+                // 更新缓存用户头像
+                loginUser.getUser().setAvatar(name);
+                tokenService.setLoginUser(loginUser);
+                return ajax;
+            }
+            return AjaxResult.error("头像上传失败.");
+        } catch (Exception e) {
+            LogUtil.error(log, e, "头像上传失败.");
+            return AjaxResult.error("头像上传失败.");
+        }
+    }
+    
+    private String commpressPicAndUpLoadOSS(String userId,MultipartFile file,String mimeType,int width,int height,String bucketName) {
+        String fileName = file.getOriginalFilename();
+        int idx = fileName.lastIndexOf('.');
+        String suffix = "";
+        if (idx > 0) {
+            suffix = fileName.substring(idx);
+        }
+        String name = userId+"/"+ UUIDUtils.newId() + suffix;
+        try {
+            QcloudFileUtils.putStream(file.getInputStream(), name, bucketName, mimeType );
+            BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
+            Builder<BufferedImage> builder = Thumbnails.of(bufferedImage);
+            int pWidth = bufferedImage.getWidth();
+            int pHeight = bufferedImage.getHeight();
+            if(pWidth > pHeight && pWidth > width) {
+                builder.height(height);
+            }else if(pHeight > pWidth && pHeight > height){
+                builder.width(480);
+            } else {
+                builder.size(pWidth, pHeight);
+            }
+            String outFileDir = filePath+"/thumb/"+userId;
+            File tempFile = new File(outFileDir);
+            if(!tempFile.exists()) {
+                tempFile.mkdirs();
+            }
+            String thumbName = name +"_s";
+            String outFilePath = filePath+"/thumb/"+thumbName+"."+mimeType.substring(mimeType.lastIndexOf("/")+1);
+            builder.toFile(outFilePath);
+            //裁剪尺寸
+            Thumbnails.of(outFilePath).scale(1f).sourceRegion(Positions.CENTER, 480,480).toFile(outFilePath);
+            File outFile = new File(outFilePath);
+            QcloudFileUtils.putFile(outFile, thumbName, bucketName, mimeType);
+            if(outFile.exists()) {
+                LogUtil.info(log, "delete file..."+outFilePath);
+                outFile.delete();
+            }
+        }  catch (Exception e) {
+            LogUtil.error(log, e, "");
+            return null;
+        }
+        return name;
+    }
+}

+ 38 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysRegisterController.java

@@ -0,0 +1,38 @@
+package com.qs.mp.web.controller.system;
+
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.domain.model.RegisterBody;
+import com.qs.mp.framework.web.service.SysRegisterService;
+import com.qs.mp.system.service.ISysConfigService;
+import com.qs.mp.web.controller.BaseController;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 注册验证
+ * 
+ * @author ygp
+ */
+@RestController
+public class SysRegisterController extends BaseController
+{
+    @Autowired
+    private SysRegisterService registerService;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @PostMapping("/register")
+    public AjaxResult register(@RequestBody RegisterBody user)
+    {
+        if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser"))))
+        {
+            return error("当前系统没有开启注册功能!");
+        }
+        String msg = registerService.register(user);
+        return StringUtils.isEmpty(msg) ? success() : error(msg);
+    }
+}

+ 257 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysRoleController.java

@@ -0,0 +1,257 @@
+package com.qs.mp.web.controller.system;
+
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.constant.UserConstants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.common.enums.RoleTypeEnum;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.core.domain.LoginUser;
+import com.qs.mp.framework.web.service.SysPermissionService;
+import com.qs.mp.framework.web.service.TokenService;
+import com.qs.mp.system.domain.SysRole;
+import com.qs.mp.system.domain.SysUser;
+import com.qs.mp.system.domain.SysUserRole;
+import com.qs.mp.system.service.ISysRoleService;
+import com.qs.mp.system.service.ISysUserService;
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 角色信息
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/role")
+public class SysRoleController extends BaseController
+{
+    @Autowired
+    private ISysRoleService roleService;
+
+    @Autowired
+    private TokenService tokenService;
+    
+    @Autowired
+    private SysPermissionService permissionService;
+    
+    @Autowired
+    private ISysUserService userService;
+
+    @PreAuthorize("@ss.hasPermi('system:role:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysRole role)
+    {
+        startPage();
+        List<SysRole> list = roleService.selectRoleList(role);
+        return getDataTable(list);
+    }
+
+    @Log(title = "角色管理", businessType = BusinessType.EXPORT)
+    @PreAuthorize("@ss.hasPermi('system:role:export')")
+    @GetMapping("/export")
+    public AjaxResult export(SysRole role)
+    {
+        List<SysRole> list = roleService.selectRoleList(role);
+        ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
+        return util.exportExcel(list, "角色数据",true);
+    }
+
+    /**
+     * 根据角色编号获取详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:query')")
+    @GetMapping(value = "/{roleId}")
+    public AjaxResult getInfo(@PathVariable Long roleId)
+    {
+        roleService.checkRoleDataScope(roleId);
+        return AjaxResult.success(roleService.selectRoleById(roleId));
+    }
+
+    /**
+     * 新增角色
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:add')")
+    @Log(title = "角色管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody SysRole role)
+    {
+//        if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role)))
+//        {
+//            return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
+//        }
+        if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role)))
+        {
+            return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
+        }
+       // RoleTypeEnum
+        if(StringUtils.isBlank(role.getRoleType())) {
+        	role.setRoleType(RoleTypeEnum.SYS_ROLE.getValue());
+        }
+        if(StringUtils.isBlank(role.getIsDefault())) {
+        	role.setIsDefault("0");
+        }
+        role.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(roleService.insertRole(role));
+
+    }
+
+    /**
+     * 修改保存角色
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
+    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody SysRole role)
+    {
+
+        roleService.checkRoleAllowed(role);
+//      if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role)))
+//      {
+//           return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
+//      }
+//      else 
+        if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role)))
+
+        {
+            return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
+        }
+        role.setUpdateBy(SecurityUtils.getUsername());
+        if(StringUtils.isBlank(role.getRoleType())) {
+        	role.setRoleType(RoleTypeEnum.SYS_ROLE.getValue());
+        }
+        if(StringUtils.isBlank(role.getIsDefault())) {
+        	role.setIsDefault("0");
+        }
+        if (roleService.updateRole(role) > 0)
+        {
+            // 更新缓存用户权限
+            LoginUser loginUser = SecurityUtils.getLoginUser();
+            if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
+            {
+                loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
+                loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
+                tokenService.setLoginUser(loginUser);
+            }
+            return AjaxResult.success();
+        }
+        return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
+    }
+
+    /**
+     * 修改保存数据权限
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
+    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/dataScope")
+    public AjaxResult dataScope(@RequestBody SysRole role)
+    {
+        roleService.checkRoleAllowed(role);
+        return toAjax(roleService.authDataScope(role));
+    }
+
+    /**
+     * 状态修改
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
+    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public AjaxResult changeStatus(@RequestBody SysRole role)
+    {
+        roleService.checkRoleAllowed(role);
+        role.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(roleService.updateRoleStatus(role));
+    }
+
+    /**
+     * 删除角色
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:remove')")
+    @Log(title = "角色管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{roleIds}")
+    public AjaxResult remove(@PathVariable Long[] roleIds)
+    {
+        return toAjax(roleService.deleteRoleByIds(roleIds));
+    }
+
+    /**
+     * 获取角色选择框列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:query')")
+    @GetMapping("/optionselect")
+    public AjaxResult optionselect()
+    {
+        return AjaxResult.success(roleService.selectRoleAll());
+    }
+
+    /**
+     * 查询已分配用户角色列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:list')")
+    @GetMapping("/authUser/allocatedList")
+    public TableDataInfo allocatedList(SysUser user)
+    {
+        startPage();
+        List<SysUser> list = userService.selectAllocatedList(user);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询未分配用户角色列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:list')")
+    @GetMapping("/authUser/unallocatedList")
+    public TableDataInfo unallocatedList(SysUser user)
+    {
+        startPage();
+        List<SysUser> list = userService.selectUnallocatedList(user);
+        return getDataTable(list);
+    }
+
+    /**
+     * 取消授权用户
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authUser/cancel")
+    public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole)
+    {
+        return toAjax(roleService.deleteAuthUser(userRole));
+    }
+
+    /**
+     * 批量取消授权用户
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authUser/cancelAll")
+    public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds)
+    {
+        return toAjax(roleService.deleteAuthUsers(roleId, userIds));
+    }
+
+    /**
+     * 批量选择用户授权
+     */
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authUser/selectAll")
+    public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds)
+    {
+        return toAjax(roleService.insertAuthUsers(roleId, userIds));
+    }
+}

+ 231 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/system/SysUserController.java

@@ -0,0 +1,231 @@
+package com.qs.mp.web.controller.system;
+
+import com.qs.mp.common.annotation.Log;
+import com.qs.mp.common.constant.UserConstants;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.system.domain.SysRole;
+import com.qs.mp.system.domain.SysUser;
+import com.qs.mp.system.service.ISysPostService;
+import com.qs.mp.system.service.ISysRoleService;
+import com.qs.mp.system.service.ISysUserService;
+import com.qs.mp.utils.ExcelUtil;
+import com.qs.mp.utils.SecurityUtils;
+import com.qs.mp.web.controller.BaseController;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 用户信息
+ * 
+ * @author ygp
+ */
+@RestController
+@RequestMapping("/system/user")
+public class SysUserController extends BaseController
+{
+    @Autowired
+    private ISysUserService userService;
+
+    @Autowired
+    private ISysRoleService roleService;
+
+    @Autowired
+    private ISysPostService postService;
+
+    /**
+     * 获取用户列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysUser user)
+    {
+        startPage();
+        List<SysUser> list = userService.selectUserList(user);
+        return getDataTable(list);
+    }
+
+    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
+    @PreAuthorize("@ss.hasPermi('system:user:export')")
+    @GetMapping("/export")
+    public AjaxResult export(SysUser user)
+    {
+        List<SysUser> list = userService.selectUserList(user);
+        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
+        return util.exportExcel(list, "用户数据",true);
+    }
+
+    @Log(title = "用户管理", businessType = BusinessType.IMPORT)
+    @PreAuthorize("@ss.hasPermi('system:user:import')")
+    @PostMapping("/importData")
+    public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
+    {
+        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
+        List<SysUser> userList = util.importExcel(file.getInputStream());
+        String operName = SecurityUtils.getUsername();
+        String message = userService.importUser(userList, updateSupport, operName);
+        return AjaxResult.success(message);
+    }
+
+    @GetMapping("/importTemplate")
+    public AjaxResult importTemplate()
+    {
+        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
+        return util.importTemplateExcel("用户数据",true);
+    }
+
+    /**
+     * 根据用户编号获取详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:query')")
+    @GetMapping(value = { "/", "/{userId}" })
+    public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
+    {
+        userService.checkUserDataScope(userId);
+        AjaxResult ajax = AjaxResult.success();
+        List<SysRole> roles = roleService.selectRoleAll();
+        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
+        ajax.put("posts", postService.selectPostAll());
+        if (StringUtils.isNotNull(userId))
+        {
+            ajax.put(AjaxResult.DATA_TAG, userService.selectUserById(userId));
+            ajax.put("postIds", postService.selectPostListByUserId(userId));
+            ajax.put("roleIds", roleService.selectRoleListByUserId(userId));
+        }
+        return ajax;
+    }
+
+    /**
+     * 新增用户
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:add')")
+    @Log(title = "用户管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody SysUser user)
+    {
+        if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName())))
+        {
+            return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
+        }
+        else if (StringUtils.isNotEmpty(user.getPhonenumber())
+                && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
+        {
+            return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
+        }
+        else if (StringUtils.isNotEmpty(user.getEmail())
+                && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
+        {
+            return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
+        }
+        user.setCreateBy(SecurityUtils.getUsername());
+        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
+        return toAjax(userService.insertUser(user));
+    }
+
+    /**
+     * 修改用户
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:edit')")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody SysUser user)
+    {
+        userService.checkUserAllowed(user);
+        if (StringUtils.isNotEmpty(user.getPhonenumber())
+                && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
+        {
+            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
+        }
+        else if (StringUtils.isNotEmpty(user.getEmail())
+                && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
+        {
+            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
+        }
+        user.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(userService.updateUser(user));
+    }
+
+    /**
+     * 删除用户
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:remove')")
+    @Log(title = "用户管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{userIds}")
+    public AjaxResult remove(@PathVariable Long[] userIds)
+    {
+        if (ArrayUtils.contains(userIds, SecurityUtils.getUserId()))
+        {
+            return error("当前用户不能删除");
+        }
+        return toAjax(userService.deleteUserByIds(userIds));
+    }
+
+    /**
+     * 重置密码
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/resetPwd")
+    public AjaxResult resetPwd(@RequestBody SysUser user)
+    {
+        userService.checkUserAllowed(user);
+        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
+        user.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(userService.resetPwd(user));
+    }
+
+    /**
+     * 状态修改
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:edit')")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public AjaxResult changeStatus(@RequestBody SysUser user)
+    {
+        userService.checkUserAllowed(user);
+        user.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(userService.updateUserStatus(user));
+    }
+
+    /**
+     * 根据用户编号获取授权角色
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:query')")
+    @GetMapping("/authRole/{userId}")
+    public AjaxResult authRole(@PathVariable("userId") Long userId)
+    {
+        AjaxResult ajax = AjaxResult.success();
+        SysUser user = userService.selectUserById(userId);
+        List<SysRole> roles = roleService.selectRolesByUserId(userId);
+        ajax.put("user", user);
+        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
+        return ajax;
+    }
+
+    /**
+     * 用户授权角色
+     */
+    @PreAuthorize("@ss.hasPermi('system:user:edit')")
+    @Log(title = "用户管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authRole")
+    public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
+    {
+        userService.insertUserAuth(userId, roleIds);
+        return success();
+    }
+}

+ 24 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/tool/SwaggerController.java

@@ -0,0 +1,24 @@
+package com.qs.mp.web.controller.tool;
+
+import com.qs.mp.web.controller.BaseController;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * swagger 接口
+ * 
+ * @author ygp
+ */
+@Controller
+@RequestMapping("/tool/swagger")
+public class SwaggerController extends BaseController
+{
+    @PreAuthorize("@ss.hasPermi('tool:swagger:view')")
+    @GetMapping()
+    public String index()
+    {
+        return redirect("/swagger-ui.html");
+    }
+}

+ 181 - 0
mp-admin/src/main/java/com/qs/mp/web/controller/tool/TestController.java

@@ -0,0 +1,181 @@
+package com.qs.mp.web.controller.tool;
+
+import com.qs.mp.web.controller.BaseController;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.utils.StringUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * swagger 用户测试方法
+ * 
+ * @author ygp
+ */
+@Api("用户信息管理")
+@RestController
+@RequestMapping("/test/user")
+public class TestController extends BaseController
+{
+    private final static Map<Integer, UserEntity> users = new LinkedHashMap<Integer, UserEntity>();
+    {
+        users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
+        users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
+    }
+
+    @ApiOperation("获取用户列表")
+    @GetMapping("/list")
+    public AjaxResult userList()
+    {
+        List<UserEntity> userList = new ArrayList<UserEntity>(users.values());
+        return AjaxResult.success(userList);
+    }
+
+    @ApiOperation("获取用户详细")
+    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path")
+    @GetMapping("/{userId}")
+    public AjaxResult getUser(@PathVariable Integer userId)
+    {
+        if (!users.isEmpty() && users.containsKey(userId))
+        {
+            return AjaxResult.success(users.get(userId));
+        }
+        else
+        {
+            return error("用户不存在");
+        }
+    }
+
+    @ApiOperation("新增用户")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer"),
+        @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String"),
+        @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String"),
+        @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String")
+    })
+    @PostMapping("/save")
+    public AjaxResult save(UserEntity user)
+    {
+        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
+        {
+            return error("用户ID不能为空");
+        }
+        return AjaxResult.success(users.put(user.getUserId(), user));
+    }
+
+    @ApiOperation("更新用户")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody UserEntity user)
+    {
+        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
+        {
+            return error("用户ID不能为空");
+        }
+        if (users.isEmpty() || !users.containsKey(user.getUserId()))
+        {
+            return error("用户不存在");
+        }
+        users.remove(user.getUserId());
+        return AjaxResult.success(users.put(user.getUserId(), user));
+    }
+
+    @ApiOperation("删除用户信息")
+    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path")
+    @DeleteMapping("/{userId}")
+    public AjaxResult delete(@PathVariable Integer userId)
+    {
+        if (!users.isEmpty() && users.containsKey(userId))
+        {
+            users.remove(userId);
+            return success();
+        }
+        else
+        {
+            return error("用户不存在");
+        }
+    }
+}
+
+@ApiModel(value = "UserEntity", description = "用户实体")
+class UserEntity
+{
+    @ApiModelProperty("用户ID")
+    private Integer userId;
+
+    @ApiModelProperty("用户名称")
+    private String username;
+
+    @ApiModelProperty("用户密码")
+    private String password;
+
+    @ApiModelProperty("用户手机")
+    private String mobile;
+
+    public UserEntity()
+    {
+
+    }
+
+    public UserEntity(Integer userId, String username, String password, String mobile)
+    {
+        this.userId = userId;
+        this.username = username;
+        this.password = password;
+        this.mobile = mobile;
+    }
+
+    public Integer getUserId()
+    {
+        return userId;
+    }
+
+    public void setUserId(Integer userId)
+    {
+        this.userId = userId;
+    }
+
+    public String getUsername()
+    {
+        return username;
+    }
+
+    public void setUsername(String username)
+    {
+        this.username = username;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+
+    public String getMobile()
+    {
+        return mobile;
+    }
+
+    public void setMobile(String mobile)
+    {
+        this.mobile = mobile;
+    }
+}

+ 9 - 0
mp-admin/src/main/java/com/qs/mp/web/core/common/ErrorConstants.java

@@ -0,0 +1,9 @@
+package com.qs.mp.web.core.common;
+
+public class ErrorConstants {
+    
+    public static final int error_code_params_null = 1001;   //请求参数为空
+    public static final int error_code_1002 = 1002;   //AppID无效
+    public static final int error_code_1003 = 1003;   //userId is null
+    public static final int error_code_1004 = 1004;   //用户信息更新失败
+}

+ 125 - 0
mp-admin/src/main/java/com/qs/mp/web/core/config/SwaggerConfig.java

@@ -0,0 +1,125 @@
+package com.qs.mp.web.core.config;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import com.qs.mp.common.config.MpConfig;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.models.auth.In;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.service.SecurityScheme;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+
+/**
+ * Swagger2的接口配置
+ * 
+ * @author ygp
+ */
+@Configuration
+public class SwaggerConfig
+{
+    /** 系统基础配置 */
+    @Autowired
+    private MpConfig mpConfig;
+
+    /** 是否开启swagger */
+    @Value("${swagger.enabled}")
+    private boolean enabled;
+
+    /** 设置请求的统一前缀 */
+    @Value("${swagger.pathMapping}")
+    private String pathMapping;
+
+    /**
+     * 创建API
+     */
+    @Bean
+    public Docket createRestApi()
+    {
+        return new Docket(DocumentationType.OAS_30)
+                // 是否启用Swagger
+                .enable(enabled)
+                // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
+                .apiInfo(apiInfo())
+                // 设置哪些接口暴露给Swagger展示
+                .select()
+                // 扫描所有有注解的api,用这种方式更灵活
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                // 扫描指定包中的swagger注解
+                // .apis(RequestHandlerSelectors.basePackage("com.qs.mp.project.tool.swagger"))
+                // 扫描所有 .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build()
+                /* 设置安全模式,swagger可以设置访问token */
+                .securitySchemes(securitySchemes())
+                .securityContexts(securityContexts())
+                .pathMapping(pathMapping);
+    }
+
+    /**
+     * 安全模式,这里指定token通过Authorization头请求头传递
+     */
+    private List<SecurityScheme> securitySchemes()
+    {
+        List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
+        apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
+        return apiKeyList;
+    }
+
+    /**
+     * 安全上下文
+     */
+    private List<SecurityContext> securityContexts()
+    {
+        List<SecurityContext> securityContexts = new ArrayList<>();
+        securityContexts.add(
+                SecurityContext.builder()
+                        .securityReferences(defaultAuth())
+                        .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
+                        .build());
+        return securityContexts;
+    }
+
+    /**
+     * 默认的安全上引用
+     */
+    private List<SecurityReference> defaultAuth()
+    {
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        List<SecurityReference> securityReferences = new ArrayList<>();
+        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
+        return securityReferences;
+    }
+
+    /**
+     * 添加摘要信息
+     */
+    private ApiInfo apiInfo()
+    {
+        // 用ApiInfoBuilder进行定制
+        return new ApiInfoBuilder()
+                // 设置标题
+                .title("标题:旺铺管家_接口文档")
+                // 描述
+                .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
+                // 作者信息
+                .contact(new Contact(mpConfig.getName(), null, null))
+                // 版本
+                .version("版本号:" + mpConfig.getVersion())
+                .build();
+    }
+}

+ 1 - 0
mp-admin/src/main/resources/META-INF/spring-devtools.properties

@@ -0,0 +1 @@
+restart.include.json=/com.alibaba.fastjson.*.jar

+ 126 - 0
mp-admin/src/main/resources/application-8100.yml

@@ -0,0 +1,126 @@
+#三方支付配置
+pay:
+  notify:
+    url: https://www.quanshu123.com
+  call:
+    backUrl: https://api.quanshu123.com/service/notify/payCallback
+  base:
+    url: https://jlpays.kakapaypay.com
+  shopNo: 1631243148392
+  sign: 121bb2b8446b48979958542749df9ee3
+
+# 日志配置
+logging:
+  config: classpath:logback-8100.xml
+  level:
+    com.qs.mp: debug
+    org.springframework: info
+    org.apache: warn
+    io.lettuce.core: warn
+    org.quartz.core: warn
+    com.qs.mp.framework.interceptor: warn
+# 数据源配置
+spring:
+    redis:
+      host: 172.17.0.17
+      port: 6379
+      password: SD232@%Gwwer1ds0(SS323
+      # 连接超时时间(毫秒)
+      timeout: 10000
+      jedis:
+        pool:
+          # 连接池中的最大空闲连接
+          max-idle: 30
+          # 连接池中的最小空闲连接
+          min-idle: 6
+          # 连接池最大连接数(使用负值表示没有限制)
+          max-active: 10
+          # 连接池最大阻塞等待时间(使用负值表示没有限制)
+          max-wait: 3000
+    datasource:
+        type: com.alibaba.druid.pool.DruidDataSource
+        driverClassName: com.mysql.cj.jdbc.Driver
+        druid:
+            # 主库数据源
+            master:
+                url: jdbc:mysql://172.17.0.3:3306/ygpdb-prod?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
+                username: ygp_prod
+                password: x23W%s2PcW23)vw23Csaw234)3
+            # 从库数据源
+            slave:
+                # 从数据源开关/默认关闭
+                enabled: false
+                url:
+                username:
+                password:
+            # 初始连接数
+            initialSize: 5
+            # 最小连接池数量
+            minIdle: 10
+            # 最大连接池数量
+            maxActive: 20
+            # 配置获取连接等待超时的时间
+            maxWait: 60000
+            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+            timeBetweenEvictionRunsMillis: 60000
+            # 配置一个连接在池中最小生存的时间,单位是毫秒
+            minEvictableIdleTimeMillis: 300000
+            # 配置一个连接在池中最大生存的时间,单位是毫秒
+            maxEvictableIdleTimeMillis: 900000
+            # 配置检测连接是否有效
+            validationQuery: SELECT 1 FROM DUAL
+            testWhileIdle: true
+            testOnBorrow: false
+            testOnReturn: false
+            webStatFilter:
+                enabled: true
+            statViewServlet:
+                enabled: true
+                # 设置白名单,不填则允许所有访问
+                allow:
+                url-pattern: /monitor/druid/*
+            filter:
+                stat:
+                    enabled: true
+                    # 慢SQL记录
+                    log-slow-sql: true
+                    slow-sql-millis: 1000
+                    merge-sql: true
+                wall:
+                    config:
+                        multi-statement-allow: true
+
+# 环境配置
+server:
+  # 服务端口
+  port: 8100
+  servlet:
+    # 项目contextPath
+    context-path: /
+  session-timeout: 1800
+  # 生成唯一ID的,同一Mac地址多应用的部署方式,用这个进行区分
+  datacenterId: 1
+  # 环境标识
+  env: prod
+
+
+#API访问
+client:
+  # appKey
+  device:
+    appKey: 3c8b36cf4ace53e76a210e5edd75ef2b
+    # appSecret
+    appSecret: b84382574b0f4988bc68367fbc340d3a
+
+
+# cloud
+cloud:
+  # 存储对象公开可访问
+  public-bucket-name: ygp-public-1307117429
+  # 存储对象公开不可访问,需要通过应用下载
+  private-bucket-name: ygp-auth-1307117429
+
+# 小程序
+miniprogram:
+  # 接收通知的状态
+  state: formal

+ 126 - 0
mp-admin/src/main/resources/application-8200.yml

@@ -0,0 +1,126 @@
+#三方支付配置
+pay:
+  notify:
+    url: https://www.quanshu123.com
+  call:
+    backUrl: https://api.quanshu123.com/service/notify/payCallback
+  base:
+    url: https://jlpays.kakapaypay.com
+  shopNo: 1631243148392
+  sign: 121bb2b8446b48979958542749df9ee3
+
+# 日志配置
+logging:
+  config: classpath:logback-8200.xml
+  level:
+    com.qs.mp: debug
+    org.springframework: info
+    org.apache: warn
+    io.lettuce.core: warn
+    org.quartz.core: warn
+    com.qs.mp.framework.interceptor: warn
+# 数据源配置
+spring:
+    redis:
+      host: 172.17.0.17
+      port: 6379
+      password: SD232@%Gwwer1ds0(SS323
+      # 连接超时时间(毫秒)
+      timeout: 10000
+      jedis:
+        pool:
+          # 连接池中的最大空闲连接
+          max-idle: 30
+          # 连接池中的最小空闲连接
+          min-idle: 6
+          # 连接池最大连接数(使用负值表示没有限制)
+          max-active: 10
+          # 连接池最大阻塞等待时间(使用负值表示没有限制)
+          max-wait: 3000
+    datasource:
+        type: com.alibaba.druid.pool.DruidDataSource
+        driverClassName: com.mysql.cj.jdbc.Driver
+        druid:
+            # 主库数据源
+            master:
+                url: jdbc:mysql://172.17.0.3:3306/ygpdb-prod?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
+                username: ygp_prod
+                password: x23W%s2PcW23)vw23Csaw234)3
+            # 从库数据源
+            slave:
+                # 从数据源开关/默认关闭
+                enabled: false
+                url:
+                username:
+                password:
+            # 初始连接数
+            initialSize: 5
+            # 最小连接池数量
+            minIdle: 10
+            # 最大连接池数量
+            maxActive: 20
+            # 配置获取连接等待超时的时间
+            maxWait: 60000
+            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+            timeBetweenEvictionRunsMillis: 60000
+            # 配置一个连接在池中最小生存的时间,单位是毫秒
+            minEvictableIdleTimeMillis: 300000
+            # 配置一个连接在池中最大生存的时间,单位是毫秒
+            maxEvictableIdleTimeMillis: 900000
+            # 配置检测连接是否有效
+            validationQuery: SELECT 1 FROM DUAL
+            testWhileIdle: true
+            testOnBorrow: false
+            testOnReturn: false
+            webStatFilter:
+                enabled: true
+            statViewServlet:
+                enabled: true
+                # 设置白名单,不填则允许所有访问
+                allow:
+                url-pattern: /monitor/druid/*
+            filter:
+                stat:
+                    enabled: true
+                    # 慢SQL记录
+                    log-slow-sql: true
+                    slow-sql-millis: 1000
+                    merge-sql: true
+                wall:
+                    config:
+                        multi-statement-allow: true
+
+# 环境配置
+server:
+  # 服务端口
+  port: 8200
+  servlet:
+    # 项目contextPath
+    context-path: /
+  session-timeout: 1800
+  # 生成唯一ID的,同一Mac地址多应用的部署方式,用这个进行区分
+  datacenterId: 1
+  # 环境标识
+  env: prod
+
+
+#API访问
+client:
+  # appKey
+  device:
+    appKey: 3c8b36cf4ace53e76a210e5edd75ef2b
+    # appSecret
+    appSecret: b84382574b0f4988bc68367fbc340d3a
+
+
+# cloud
+cloud:
+  # 存储对象公开可访问
+  public-bucket-name: ygp-public-1307117429
+  # 存储对象公开不可访问,需要通过应用下载
+  private-bucket-name: ygp-auth-1307117429
+
+# 小程序
+miniprogram:
+  # 接收通知的状态
+  state: formal

+ 120 - 0
mp-admin/src/main/resources/application-dev.yml

@@ -0,0 +1,120 @@
+pay:
+    notify:
+        url: https://www.quanshu123.com
+    call:
+        backUrl: https://api.quanshu123.com/service/notify/payCallback
+    base:
+        url: https://jlpays.kakapaypay.com
+    shopNo: 1631243148392
+    sign: 121bb2b8446b48979958542749df9ee3
+
+# 数据源配置
+spring:
+    redis:
+        host: 113.31.162.168
+        port: 6379
+        password: CjPfyr77Bbdfg
+        # 连接超时时间(毫秒)
+        timeout: 10000
+        jedis:
+            pool:
+                # 连接池中的最大空闲连接
+                max-idle: 30
+                # 连接池中的最小空闲连接
+                min-idle: 6
+                # 连接池最大连接数(使用负值表示没有限制)
+                max-active: 10
+                # 连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: 3000
+    datasource:
+        type: com.alibaba.druid.pool.DruidDataSource
+        driverClassName: com.mysql.cj.jdbc.Driver
+        druid:
+            # 主库数据源
+            master:
+                url: jdbc:mysql://113.31.162.168:3306/mpdb_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
+                username: mptest
+                password: wan789*@dfhzHu518!dr2xosn
+                # 从库数据源
+            slave:
+                # 从数据源开关/默认关闭
+                enabled: false
+                url: 
+                username: 
+                password: 
+            # 初始连接数
+            initialSize: 5
+            # 最小连接池数量
+            minIdle: 10
+            # 最大连接池数量
+            maxActive: 20
+            # 配置获取连接等待超时的时间
+            maxWait: 60000
+            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+            timeBetweenEvictionRunsMillis: 60000
+            # 配置一个连接在池中最小生存的时间,单位是毫秒
+            minEvictableIdleTimeMillis: 300000
+            # 配置一个连接在池中最大生存的时间,单位是毫秒
+            maxEvictableIdleTimeMillis: 900000
+            # 配置检测连接是否有效
+            validationQuery: SELECT 1 FROM DUAL
+            testWhileIdle: true
+            testOnBorrow: false
+            testOnReturn: false
+            webStatFilter: 
+                enabled: true
+            statViewServlet:
+                enabled: true
+                # 设置白名单,不填则允许所有访问
+                allow:
+                url-pattern: /druid/*
+                # 控制台管理用户名和密码
+                login-username: mp
+                login-password: 123456
+            filter:
+                stat:
+                    enabled: true
+                    # 慢SQL记录
+                    log-slow-sql: true
+                    slow-sql-millis: 1000
+                    merge-sql: true
+                wall:
+                    config:
+                        multi-statement-allow: true
+# 日志配置
+logging:
+    config: classpath:logback-spring.xml
+    level:
+        com.qs.mp: debug
+        org.springframework: warn
+        org.apache: warn
+        io.lettuce.core: warn
+        com.qs.mp.framework.interceptor: warn
+
+# 开发环境配置
+server:
+    # 生成唯一ID的,同一Mac地址多应用的部署方式,用这个进行区分
+    datacenterId: 1
+    # 环境标识
+    env: dev
+
+#API访问
+client:
+    # appKey
+    device:
+        appKey: 3c8b36cf4ace53e76a210e5edd75ef2b
+        # appSecret
+        appSecret: b84382574b0f4988bc68367fbc340d3a
+
+# cloud
+cloud:
+    # 存储对象公开可访问
+    public-bucket-name: ygp-public-test-1307117429
+    # 存储对象公开不可访问,需要通过应用下载
+    private-bucket-name: ygp-auth-test-1307117429
+
+# 小程序
+miniprogram:
+    # 接收通知的状态
+    state: developer
+

+ 124 - 0
mp-admin/src/main/resources/application-test.yml

@@ -0,0 +1,124 @@
+pay:
+  notify:
+    url: https://www.quanshu123.com
+  call:
+    backUrl: https://test-api.quanshu123.com/service/notify/payCallback
+  base:
+    url: https://jlpays.kakapaypay.com
+  shopNo: 1631243148392
+  sign: 121bb2b8446b48979958542749df9ee3
+
+# 日志配置
+logging:
+  config: classpath:logback-8090.xml
+  level:
+    com.qs.mp: debug
+    org.springframework: warn
+    org.apache: warn
+    io.lettuce.core: warn
+    org.quartz.core: warn
+    com.qs.mp.framework.interceptor: warn
+# 数据源配置
+spring:
+    redis:
+      host: 113.31.162.168
+      port: 6379
+      password: CjPfyr77Bbdfg
+      # 连接超时时间(毫秒)
+      timeout: 10000
+      jedis:
+        pool:
+          # 连接池中的最大空闲连接
+          max-idle: 30
+          # 连接池中的最小空闲连接
+          min-idle: 6
+          # 连接池最大连接数(使用负值表示没有限制)
+          max-active: 10
+          # 连接池最大阻塞等待时间(使用负值表示没有限制)
+          max-wait: 3000
+    datasource:
+        type: com.alibaba.druid.pool.DruidDataSource
+        driverClassName: com.mysql.cj.jdbc.Driver
+        druid:
+            # 主库数据源
+            master:
+                url: jdbc:mysql://113.31.162.168:3306/mpdb_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
+                username: mptest
+                password: wan789*@dfhzHu518!dr2xosn
+            # 从库数据源
+            slave:
+                # 从数据源开关/默认关闭
+                enabled: false
+                url:
+                username:
+                password:
+            # 初始连接数
+            initialSize: 5
+            # 最小连接池数量
+            minIdle: 10
+            # 最大连接池数量
+            maxActive: 20
+            # 配置获取连接等待超时的时间
+            maxWait: 60000
+            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+            timeBetweenEvictionRunsMillis: 60000
+            # 配置一个连接在池中最小生存的时间,单位是毫秒
+            minEvictableIdleTimeMillis: 300000
+            # 配置一个连接在池中最大生存的时间,单位是毫秒
+            maxEvictableIdleTimeMillis: 900000
+            # 配置检测连接是否有效
+            validationQuery: SELECT 1 FROM DUAL
+            testWhileIdle: true
+            testOnBorrow: false
+            testOnReturn: false
+            webStatFilter:
+                enabled: true
+            statViewServlet:
+                enabled: true
+                # 设置白名单,不填则允许所有访问
+                allow:
+                url-pattern: /monitor/druid/*
+            filter:
+                stat:
+                    enabled: true
+                    # 慢SQL记录
+                    log-slow-sql: true
+                    slow-sql-millis: 1000
+                    merge-sql: true
+                wall:
+                    config:
+                        multi-statement-allow: true
+
+# 环境配置
+server:
+  # 服务端口
+  port: 8090
+  servlet:
+    # 项目contextPath
+    context-path: /
+  session-timeout: 1800
+  # 生成唯一ID的,同一Mac地址多应用的部署方式,用这个进行区分
+  datacenterId: 1
+  # 环境标识
+  env: test
+
+
+#API访问
+client:
+  # appKey
+  device:
+    appKey: 3c8b36cf4ace53e76a210e5edd75ef2b
+    # appSecret
+    appSecret: b84382574b0f4988bc68367fbc340d3a
+
+# cloud
+cloud:
+  # 存储对象公开可访问
+  public-bucket-name: ygp-public-test-1307117429
+  # 存储对象公开不可访问,需要通过应用下载
+  private-bucket-name: ygp-auth-test-1307117429
+
+# 小程序
+miniprogram:
+  # 接收通知的状态
+  state: developer

+ 134 - 0
mp-admin/src/main/resources/application.yml

@@ -0,0 +1,134 @@
+# 项目相关配置
+mp:
+  # 名称
+  name: mp
+  # 版本
+  version: 1.0.0
+  # 版权年份
+  copyrightYear: 2022
+  # 实例演示开关
+  demoEnabled: true
+  # 文件路径 示例( Windows配置D:/ygp/uploadPath,Linux配置 /home/ygp/uploadPath)
+  profile: /Users/steven/Documents
+  # 获取ip地址开关
+  addressEnabled: false
+  # 验证码类型 math 数组计算 char 字符验证
+  captchaType: math
+
+# 开发环境配置
+server:
+  # 服务器的HTTP端口,默认为8080
+  port: 8080
+  servlet:
+    # 应用的访问路径
+    context-path: /
+
+# Spring配置
+spring:
+  # 资源信息
+  messages:
+    # 国际化资源文件路径
+    basename: i18n/messages
+  profiles: 
+    active: dev
+  # 文件上传
+  servlet:
+     multipart:
+       # 单个文件大小
+       max-file-size:  10MB
+       # 设置总上传的文件大小
+       max-request-size:  20MB
+  # 服务模块
+  devtools:
+    restart:
+      # 热部署开关
+      enabled: true
+  # redis 配置
+  redis:
+    # 地址
+    host: localhost
+    # 端口,默认为6379
+    port: 6379
+    # 数据库索引
+    database: 0
+    # 密码
+    password: 
+    # 连接超时时间
+    timeout: 10s
+    lettuce:
+      pool:
+        # 连接池中的最小空闲连接
+        min-idle: 0
+        # 连接池中的最大空闲连接
+        max-idle: 8
+        # 连接池的最大数据库连接数
+        max-active: 8
+        # #连接池最大阻塞等待时间(使用负值表示没有限制)
+        max-wait: -1ms
+
+# token配置
+token:
+    # 令牌自定义标识
+    header: Authorization
+    # 令牌密钥
+    secret: abcdefghijklmnopqrstuvwxyz
+    # 令牌有效期(默认30分钟)
+    expireTime: 10080
+
+
+# PageHelper分页插件
+pagehelper: 
+  helperDialect: mysql
+  supportMethodsArguments: true
+  params: count=countSql 
+
+# Swagger配置
+swagger:
+  # 是否开启swagger
+  enabled: true
+  # 请求前缀
+  pathMapping: /dev-api
+
+# 防止XSS攻击
+xss: 
+  # 过滤开关
+  enabled: true
+  # 排除链接(多个用逗号分隔)
+  excludes: /system/notice
+  # 匹配链接
+  urlPatterns: /system/*,/monitor/*,/tool/*,/api/*
+
+# Mybatis-Plus
+mybatis-plus:
+  # Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)
+  mapper-locations: classpath*:/mapper/**/*Mapper.xml
+  global-config:
+    db-config:
+      property-format: "\"%s\""
+      logic-delete-value: 1
+      logic-not-delete-value: 0
+  type-enums-package: com.qs.mp.common.enums
+  configuration:
+    # 3.0.8之前版本问题默认将枚举注册为EnumOrdinalTypeHandler,这是错误的方式,默认是 org.apache.ibatis.type.EnumTypeHandler
+    # 如果项目之中实体统一使用IEnum或注解的方式,可配置成 com.baomidou.mybatisplus.extension.handlers.EnumTypeHandler,也可省略上面的type-enums-package配置
+    # 配置type-enums-package只对注解方式的枚举处理能提前加载缓存.
+    default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler
+    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
+
+# cloud
+cloud:
+  # 存储对象公开可访问
+  #public-bucket-name: ygp-public
+  # 存储对象公开不可访问,需要通过应用下载
+  #private-bucket-name: ygp-auth
+  # 合同模板存放目录
+  template-dir: files/template/
+  # 签署好的合同文件存放目录
+  contract-dir: files/contract/
+
+wx:
+  appId: wxb86cb7f459fc3675
+  appSecret: 5413864d90c2a447f8f3e4ae897dfae2
+
+wxgzh:
+  appId: wxfe9785e665c741a2

+ 24 - 0
mp-admin/src/main/resources/banner.txt

@@ -0,0 +1,24 @@
+Application Version: ${mp.version}
+Spring Boot Version: ${spring-boot.version}
+////////////////////////////////////////////////////////////////////
+//                          _ooOoo_                               //
+//                         o8888888o                              //
+//                         88" . "88                              //
+//                         (| ^_^ |)                              //
+//                         O\  =  /O                              //
+//                      ____/`---'\____                           //
+//                    .'  \\|     |//  `.                         //
+//                   /  \\|||  :  |||//  \                        //
+//                  /  _||||| -:- |||||-  \                       //
+//                  |   | \\\  -  /// |   |                       //
+//                  | \_|  ''\---/''  |   |                       //
+//                  \  .-\__  `-`  ___/-. /                       //
+//                ___`. .'  /--.--\  `. . ___                     //
+//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
+//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
+//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
+//      ========`-.____`-.___\_____/___.-`____.-'========         //
+//                           `=---='                              //
+//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
+//             佛祖保佑       永不宕机      永无BUG               //
+////////////////////////////////////////////////////////////////////

+ 37 - 0
mp-admin/src/main/resources/i18n/messages.properties

@@ -0,0 +1,37 @@
+#错误消息
+not.null=* 必须填写
+user.jcaptcha.error=验证码错误
+user.jcaptcha.expire=验证码已失效
+user.not.exists=用户不存在/密码错误
+user.password.not.match=用户不存在/密码错误
+user.password.retry.limit.count=密码输入错误{0}次
+user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟
+user.password.delete=对不起,您的账号已被删除
+user.blocked=用户已封禁,请联系管理员
+role.blocked=角色已封禁,请联系管理员
+user.logout.success=退出成功
+
+length.not.valid=长度必须在{min}到{max}个字符之间
+
+user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
+user.password.not.valid=* 5-50个字符
+ 
+user.email.not.valid=邮箱格式错误
+user.mobile.phone.number.not.valid=手机号格式错误
+user.login.success=登录成功
+user.register.success=注册成功
+user.notfound=请重新登录
+user.forcelogout=管理员强制退出,请重新登录
+user.unknown.error=未知错误,请重新登录
+
+##文件上传消息
+upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
+upload.filename.exceed.length=上传的文件名最长{0}个字符
+
+##权限
+no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
+no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
+no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
+no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
+no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
+no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]

+ 66 - 0
mp-admin/src/main/resources/logback-8090.xml

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/quanshu/mp-server" />
+    <!-- 日志输出格式 -->
+    <!-- You can override this to have a custom pattern -->
+	<property name="CONSOLE_LOG_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+    <!-- file logger config -->
+    <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/out.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/out.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+    <!-- console logger out -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+    </appender>
+
+    <root level="DEBUG">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="DEBUG"/>
+        <appender-ref ref="ERROR" />
+    </root>
+
+</configuration> 

+ 70 - 0
mp-admin/src/main/resources/logback-8100.xml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/quanshu/mp-server/logs/8100" />
+    <!-- 日志输出格式 -->
+    <!-- You can override this to have a custom pattern -->
+	<property name="CONSOLE_LOG_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+    <!-- file logger config -->
+    <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/out.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/out.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+    <appender name="SendErrorMsgAppender"
+      class="com.qs.mp.framework.monitor.SendErrorMsgAppender"></appender>
+    <!-- console logger out -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+    </appender>
+
+
+    <root level="DEBUG">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="INFO"/>
+        <appender-ref ref="ERROR" />
+        <appender-ref ref="SendErrorMsgAppender"/>
+    </root>
+
+</configuration> 

+ 70 - 0
mp-admin/src/main/resources/logback-8200.xml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/quanshu/mp-server/logs/8200" />
+    <!-- 日志输出格式 -->
+    <!-- You can override this to have a custom pattern -->
+	<property name="CONSOLE_LOG_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+    <!-- file logger config -->
+    <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/out.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/out.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+    <appender name="SendErrorMsgAppender"
+      class="com.qs.mp.framework.monitor.SendErrorMsgAppender"></appender>
+    <!-- console logger out -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+    </appender>
+
+
+    <root level="DEBUG">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="INFO"/>
+        <appender-ref ref="ERROR" />
+        <appender-ref ref="SendErrorMsgAppender"/>
+    </root>
+
+</configuration> 

+ 66 - 0
mp-admin/src/main/resources/logback-spring.xml

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/Users/steven/Documents/logs" />
+    <!-- 日志输出格式 -->
+    <!-- You can override this to have a custom pattern -->
+	<property name="CONSOLE_LOG_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+    <!-- file logger config -->
+
+    <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/out.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/out.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>DEBUG</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+    <!-- console logger out -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+    </appender>
+
+    <root level="DEBUG">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="DEBUG"/>
+        <appender-ref ref="ERROR" />
+    </root>
+</configuration> 

+ 15 - 0
mp-admin/src/main/resources/mybatis/mybatis-config.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration
+PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-config.dtd">
+<configuration>
+	
+	<settings>
+		<setting name="cacheEnabled"             value="true" />  <!-- 全局映射器启用缓存 -->
+		<setting name="useGeneratedKeys"         value="true" />  <!-- 允许 JDBC 支持自动生成主键 -->
+		<setting name="defaultExecutorType"      value="REUSE" /> <!-- 配置默认的执行器 -->
+		<setting name="logImpl"                  value="SLF4J" /> <!-- 指定 MyBatis 所用日志的具体实现 -->
+		<!-- <setting name="mapUnderscoreToCamelCase" value="true"/>  驼峰式命名 -->
+	</settings>
+	
+</configuration>

+ 103 - 0
mp-common/.factorypath

@@ -0,0 +1,103 @@
+<factorypath>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context-support/5.2.12.RELEASE/spring-context-support-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-beans/5.2.12.RELEASE/spring-beans-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context/5.2.12.RELEASE/spring-context-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-expression/5.2.12.RELEASE/spring-expression-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-core/5.2.12.RELEASE/spring-core-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jcl/5.2.12.RELEASE/spring-jcl-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-web/5.2.12.RELEASE/spring-web-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-security/2.2.13.RELEASE/spring-boot-starter-security-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter/2.2.13.RELEASE/spring-boot-starter-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot/2.2.13.RELEASE/spring-boot-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-logging/2.2.13.RELEASE/spring-boot-starter-logging-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-to-slf4j/2.12.1/log4j-to-slf4j-2.12.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aop/5.2.12.RELEASE/spring-aop-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-config/5.2.8.RELEASE/spring-security-config-5.2.8.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-core/5.2.8.RELEASE/spring-security-core-5.2.8.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/security/spring-security-web/5.2.8.RELEASE/spring-security-web-5.2.8.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/pagehelper/pagehelper-spring-boot-starter/1.3.1/pagehelper-spring-boot-starter-1.3.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/spring/boot/mybatis-spring-boot-starter/2.1.4/mybatis-spring-boot-starter-2.1.4.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.1.4/mybatis-spring-boot-autoconfigure-2.1.4.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/mybatis/3.5.6/mybatis-3.5.6.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/pagehelper/pagehelper-spring-boot-autoconfigure/1.3.1/pagehelper-spring-boot-autoconfigure-1.3.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/pagehelper/pagehelper/5.2.1/pagehelper-5.2.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/jsqlparser/jsqlparser/4.0/jsqlparser-4.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.10.5.1/jackson-databind-2.10.5.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.10.5/jackson-annotations-2.10.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.10.5/jackson-core-2.10.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/alibaba/fastjson/1.2.76/fastjson-1.2.76.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/commons-fileupload/commons-fileupload/1.4/commons-fileupload-1.4.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/poi/poi-ooxml/4.1.2/poi-ooxml-4.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/poi/poi/4.1.2/poi-4.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/commons-codec/commons-codec/1.13/commons-codec-1.13.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-collections4/4.4/commons-collections4-4.4.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/zaxxer/SparseBitSet/1.2/SparseBitSet-1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/poi/poi-ooxml-schemas/4.1.2/poi-ooxml-schemas-4.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/xmlbeans/xmlbeans/3.1.0/xmlbeans-3.1.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-compress/1.19/commons-compress-1.19.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/virtuald/curvesapi/1.06/curvesapi-1.06.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/jsonwebtoken/jjwt/0.9.1/jjwt-0.9.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-data-redis/2.2.13.RELEASE/spring-boot-starter-data-redis-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-redis/2.2.12.RELEASE/spring-data-redis-2.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-keyvalue/2.2.12.RELEASE/spring-data-keyvalue-2.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-commons/2.2.12.RELEASE/spring-data-commons-2.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-tx/5.2.12.RELEASE/spring-tx-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-oxm/5.2.12.RELEASE/spring-oxm-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/lettuce/lettuce-core/5.2.2.RELEASE/lettuce-core-5.2.2.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-common/4.1.58.Final/netty-common-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-handler/4.1.58.Final/netty-handler-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-resolver/4.1.58.Final/netty-resolver-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-buffer/4.1.58.Final/netty-buffer-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-codec/4.1.58.Final/netty-codec-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-transport/4.1.58.Final/netty-transport-4.1.58.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/projectreactor/reactor-core/3.3.13.RELEASE/reactor-core-3.3.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/reactivestreams/reactive-streams/1.0.3/reactive-streams-1.0.3.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-pool2/2.7.0/commons-pool2-2.7.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/eu/bitwalker/UserAgentUtils/1.21/UserAgentUtils-1.21.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-boot-starter/3.3.0/mybatis-plus-boot-starter-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus/3.3.0/mybatis-plus-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-extension/3.3.0/mybatis-plus-extension-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-core/3.3.0/mybatis-plus-core-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-annotation/3.3.0/mybatis-plus-annotation-3.3.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-autoconfigure/2.2.13.RELEASE/spring-boot-autoconfigure-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-jdbc/2.2.13.RELEASE/spring-boot-starter-jdbc-2.2.13.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/zaxxer/HikariCP/3.4.5/HikariCP-3.4.5.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jdbc/5.2.12.RELEASE/spring-jdbc-5.2.12.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/mybatis-spring/2.0.0/mybatis-spring-2.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/cn/hutool/hutool-all/5.3.1/hutool-all-5.3.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/projectlombok/lombok/1.18.10/lombok-1.18.10.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-boot-starter/3.0.0/springfox-boot-starter-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-oas/3.0.0/springfox-oas-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-annotations/2.1.2/swagger-annotations-2.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-models/2.1.2/swagger-models-2.1.2.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-spi/3.0.0/springfox-spi-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-schema/3.0.0/springfox-schema-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-core/3.0.0/springfox-core-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/net/bytebuddy/byte-buddy/1.10.19/byte-buddy-1.10.19.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-spring-web/3.0.0/springfox-spring-web-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/github/classgraph/classgraph/4.8.83/classgraph-4.8.83.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-spring-webmvc/3.0.0/springfox-spring-webmvc-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-spring-webflux/3.0.0/springfox-spring-webflux-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-swagger-common/3.0.0/springfox-swagger-common-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/mapstruct/mapstruct/1.3.1.Final/mapstruct-1.3.1.Final.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-data-rest/3.0.0/springfox-data-rest-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-bean-validators/3.0.0/springfox-bean-validators-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-swagger2/3.0.0/springfox-swagger2-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/swagger-annotations/1.5.20/swagger-annotations-1.5.20.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/io/springfox/springfox-swagger-ui/3.0.0/springfox-swagger-ui-3.0.0.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/plugin/spring-plugin-core/2.0.0.RELEASE/spring-plugin-core-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/plugin/spring-plugin-metadata/2.0.0.RELEASE/spring-plugin-metadata-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
+</factorypath>

+ 198 - 0
mp-common/pom.xml

@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>mp</artifactId>
+        <groupId>com.qs.mp</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>mp-common</artifactId>
+
+    <description>
+        common通用工具
+    </description>
+    
+     <properties>
+        <hutool.version>5.3.1</hutool.version>
+        <lombok.version>1.18.10</lombok.version>
+         <swagger.version>3.0.0</swagger.version>
+    </properties>
+
+    <dependencies>
+
+        <!-- Spring框架基本的核心工具 -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context-support</artifactId>
+        </dependency>
+
+        <!-- SpringWeb模块 -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+        </dependency>
+
+        <!-- spring security 安全认证 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <!-- pagehelper 分页插件 -->
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- 自定义验证注解 -->
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
+        <!--常用工具类 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+  
+        <!-- JSON工具类 -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-xml</artifactId>
+        </dependency>
+        <!-- 阿里JSON解析器 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
+        <!-- io常用工具类 -->
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+
+        <!-- 文件上传工具类 -->
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.qcloud</groupId>
+            <artifactId>cos_api</artifactId>
+        </dependency>
+
+        <!-- 短信发送 -->
+        <dependency>
+            <groupId>cn.jpush.api</groupId>
+            <artifactId>jiguang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.jpush.api</groupId>
+            <artifactId>jsms-client</artifactId>
+        </dependency>
+
+        <!--图片压缩工具 -->
+        <dependency>
+            <groupId>net.coobird</groupId>
+            <artifactId>thumbnailator</artifactId>
+        </dependency>
+
+        <!-- excel工具 -->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+        </dependency>
+
+        <!-- yml解析器 -->
+        <dependency>
+            <groupId>org.yaml</groupId>
+            <artifactId>snakeyaml</artifactId>
+        </dependency>
+
+        <!--Token生成与解析-->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+        </dependency>
+
+        <!-- redis 缓存操作 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <!-- pool 对象池 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+
+        <!-- 解析客户端操作系统、浏览器等 -->
+        <dependency>
+            <groupId>eu.bitwalker</groupId>
+            <artifactId>UserAgentUtils</artifactId>
+        </dependency>
+
+		<dependency>
+			<groupId>com.squareup.okhttp3</groupId>
+			<artifactId>okhttp</artifactId>
+			<version>3.10.0</version>
+		</dependency>
+		
+        <!-- servlet包 -->
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+        
+        <!--工具包-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
+        
+                    
+        <!--使用 lombok 简化 Java 代码-->
+        <dependency>
+           <groupId>org.projectlombok</groupId>
+           <artifactId>lombok</artifactId>
+           <version>${lombok.version}</version>
+           <scope>compile</scope>
+        </dependency>
+        
+         <!-- Swagger3依赖 -->
+         <dependency>
+             <groupId>io.springfox</groupId>
+             <artifactId>springfox-boot-starter</artifactId>
+             <version>${swagger.version}</version>
+             <exclusions>
+                 <exclusion>
+                     <groupId>io.swagger</groupId>
+                     <artifactId>swagger-models</artifactId>
+                 </exclusion>
+             </exclusions>
+         </dependency>
+
+    </dependencies>
+
+</project>

+ 28 - 0
mp-common/src/main/java/com/qs/mp/common/annotation/DataScope.java

@@ -0,0 +1,28 @@
+package com.qs.mp.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 数据权限过滤注解
+ * 
+ * @author ygp
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataScope
+{
+    /**
+     * 部门表的别名
+     */
+    public String deptAlias() default "";
+
+    /**
+     * 用户表的别名
+     */
+    public String userAlias() default "";
+}

+ 28 - 0
mp-common/src/main/java/com/qs/mp/common/annotation/DataSource.java

@@ -0,0 +1,28 @@
+package com.qs.mp.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import com.qs.mp.common.enums.DataSourceType;
+
+/**
+ * 自定义多数据源切换注解
+ *
+ * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
+ *
+ * @author ygp
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface DataSource
+{
+    /**
+     * 切换数据源名称
+     */
+    public DataSourceType value() default DataSourceType.MASTER;
+}

+ 170 - 0
mp-common/src/main/java/com/qs/mp/common/annotation/Excel.java

@@ -0,0 +1,170 @@
+package com.qs.mp.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+
+/**
+ * 自定义导出Excel数据注解
+ * 
+ * @author ygp
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Excel
+{
+    /**
+     * 导出时在excel中排序
+     */
+    public int sort() default Integer.MAX_VALUE;
+
+    /**
+     * 导出到Excel中的名字.
+     */
+    public String name() default "";
+
+    /**
+     * 日期格式, 如: yyyy-MM-dd
+     */
+    public String dateFormat() default "yyyy/MM/dd";
+
+    /**
+     * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
+     */
+    public String dictType() default "";
+
+    /**
+     * 读取内容转表达式 (如: 0=男,1=女,2=未知)
+     */
+    public String readConverterExp() default "";
+
+    /**
+     * 分隔符,读取字符串组内容
+     */
+    public String separator() default ",";
+
+    /**
+     * 是否转换成元
+     */
+    public boolean isCentToYuan() default false;
+
+    /**
+     * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
+     */
+    public int scale() default -1;
+
+    /**
+     * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
+     */
+    public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
+
+    /**
+     * 导出类型(0数字 1字符串)
+     */
+    public ColumnType cellType() default ColumnType.STRING;
+
+    /**
+     * 导出时在excel中每个列的高度 单位为字符
+     */
+    public double height() default 14;
+
+    /**
+     * 导出时在excel中每个列的宽 单位为字符
+     */
+    public double width() default 16;
+
+    /**
+     * 文字后缀,如% 90 变成90%
+     */
+    public String suffix() default "";
+
+    /**
+     * 当值为空时,字段的默认值
+     */
+    public String defaultValue() default "";
+
+    /**
+     * 提示信息
+     */
+    public String prompt() default "";
+
+    /**
+     * 设置只能选择不能输入的列内容.
+     */
+    public String[] combo() default {};
+
+    /**
+     * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
+     */
+    public boolean isExport() default true;
+
+    /**
+     * 另一个类中的属性名称,支持多级获取,以小数点隔开
+     */
+    public String targetAttr() default "";
+
+    /**
+     * 是否自动统计数据,在最后追加一行统计数据总和
+     */
+    public boolean isStatistics() default false;
+
+    /**
+     * 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右)
+     */
+    Align align() default Align.AUTO;
+
+    public enum Align
+    {
+        AUTO(0), LEFT(1), CENTER(2), RIGHT(3);
+        private final int value;
+
+        Align(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
+
+    /**
+     * 字段类型(0:导出导入;1:仅导出;2:仅导入)
+     */
+    Type type() default Type.ALL;
+
+    public enum Type
+    {
+        ALL(0), EXPORT(1), IMPORT(2);
+        private final int value;
+
+        Type(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
+
+    public enum ColumnType
+    {
+        NUMERIC(0), STRING(1), IMAGE(2);
+        private final int value;
+
+        ColumnType(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
+}

+ 18 - 0
mp-common/src/main/java/com/qs/mp/common/annotation/Excels.java

@@ -0,0 +1,18 @@
+package com.qs.mp.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Excel注解集
+ * 
+ * @author ygp
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Excels
+{
+    public Excel[] value();
+}

+ 41 - 0
mp-common/src/main/java/com/qs/mp/common/annotation/Log.java

@@ -0,0 +1,41 @@
+package com.qs.mp.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import com.qs.mp.common.enums.BusinessType;
+import com.qs.mp.common.enums.OperatorType;
+
+/**
+ * 自定义操作日志记录注解
+ * 
+ * @author ygp
+ *
+ */
+@Target({ ElementType.PARAMETER, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Log
+{
+    /**
+     * 模块 
+     */
+    public String title() default "";
+
+    /**
+     * 功能
+     */
+    public BusinessType businessType() default BusinessType.OTHER;
+
+    /**
+     * 操作人类别
+     */
+    public OperatorType operatorType() default OperatorType.MANAGE;
+
+    /**
+     * 是否保存请求的参数
+     */
+    public boolean isSaveRequestData() default true;
+}

+ 40 - 0
mp-common/src/main/java/com/qs/mp/common/annotation/RateLimiter.java

@@ -0,0 +1,40 @@
+package com.qs.mp.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import com.qs.mp.common.constant.Constants;
+import com.qs.mp.common.enums.LimitType;
+
+/**
+ * 限流注解
+ * 
+ * @author ygp
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RateLimiter
+{
+    /**
+     * 限流key
+     */
+    public String key() default Constants.RATE_LIMIT_KEY;
+
+    /**
+     * 限流时间,单位秒
+     */
+    public int time() default 60;
+
+    /**
+     * 限流次数
+     */
+    public int count() default 100;
+
+    /**
+     * 限流类型
+     */
+    public LimitType limitType() default LimitType.DEFAULT;
+}

+ 23 - 0
mp-common/src/main/java/com/qs/mp/common/annotation/RepeatSubmit.java

@@ -0,0 +1,23 @@
+package com.qs.mp.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 自定义注解防止表单重复提交
+ * 
+ * @author ygp
+ *
+ */
+@Inherited
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RepeatSubmit
+{
+
+}

+ 124 - 0
mp-common/src/main/java/com/qs/mp/common/config/MpConfig.java

@@ -0,0 +1,124 @@
+package com.qs.mp.common.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 读取项目相关配置
+ * 
+ * @author ygp
+ */
+@Component
+@ConfigurationProperties(prefix = "mp")
+public class MpConfig
+{
+    /** 项目名称 */
+    private String name;
+
+    /** 版本 */
+    private String version;
+
+    /** 版权年份 */
+    private String copyrightYear;
+
+    /** 实例演示开关 */
+    private boolean demoEnabled;
+
+    /** 上传路径 */
+    private static String profile;
+
+    /** 获取地址开关 */
+    private static boolean addressEnabled;
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getVersion()
+    {
+        return version;
+    }
+
+    public void setVersion(String version)
+    {
+        this.version = version;
+    }
+
+    public String getCopyrightYear()
+    {
+        return copyrightYear;
+    }
+
+    public void setCopyrightYear(String copyrightYear)
+    {
+        this.copyrightYear = copyrightYear;
+    }
+
+    public boolean isDemoEnabled()
+    {
+        return demoEnabled;
+    }
+
+    public void setDemoEnabled(boolean demoEnabled)
+    {
+        this.demoEnabled = demoEnabled;
+    }
+
+    public static String getProfile()
+    {
+        return profile;
+    }
+
+    public void setProfile(String profile)
+    {
+    	MpConfig.profile = profile;
+    }
+
+    public static boolean isAddressEnabled()
+    {
+        return addressEnabled;
+    }
+
+    public void setAddressEnabled(boolean addressEnabled)
+    {
+    	MpConfig.addressEnabled = addressEnabled;
+    }
+
+    /**
+     * 获取导入上传路径
+     */
+    public static String getImportPath()
+    {
+        return getProfile() + "/import";
+    }
+
+    /**
+     * 获取头像上传路径
+     */
+    public static String getAvatarPath()
+    {
+        return getProfile() + "/avatar";
+    }
+
+    /**
+     * 获取下载路径
+     */
+    public static String getDownloadPath()
+    {
+        return getProfile() + "/download/";
+    }
+
+    /**
+     * 获取上传路径
+     */
+    public static String getUploadPath()
+    {
+        return getProfile() + "/upload";
+    }
+}

+ 156 - 0
mp-common/src/main/java/com/qs/mp/common/constant/Constants.java

@@ -0,0 +1,156 @@
+package com.qs.mp.common.constant;
+
+import io.jsonwebtoken.Claims;
+
+/**
+ * 通用常量信息
+ * 
+ * @author ygp
+ */
+public class Constants
+{
+    /**
+     * UTF-8 字符集
+     */
+    public static final String UTF8 = "UTF-8";
+
+    /**
+     * GBK 字符集
+     */
+    public static final String GBK = "GBK";
+
+    /**
+     * http请求
+     */
+    public static final String HTTP = "http://";
+
+    /**
+     * https请求
+     */
+    public static final String HTTPS = "https://";
+
+    /**
+     * 通用成功标识
+     */
+    public static final String SUCCESS = "0";
+
+    /**
+     * 通用失败标识
+     */
+    public static final String FAIL = "1";
+
+    /**
+     * 登录成功
+     */
+    public static final String LOGIN_SUCCESS = "Success";
+
+    /**
+     * 注销
+     */
+    public static final String LOGOUT = "Logout";
+
+    /**
+     * 注册
+     */
+    public static final String REGISTER = "Register";
+
+    /**
+     * 登录失败
+     */
+    public static final String LOGIN_FAIL = "Error";
+
+    /**
+     * 验证码 redis key
+     */
+    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+
+    /**
+     * 短信验证码 redis key
+     */
+    public static final String SMS_CAPTCHA_CODE_KEY = "sms_captcha_codes:";
+
+    /**
+     * 登录用户 redis key
+     */
+    public static final String LOGIN_TOKEN_KEY = "login_tokens:";
+
+    /**
+     * 防重提交 redis key
+     */
+    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
+
+    /**
+     * 限流 redis key
+     */
+    public static final String RATE_LIMIT_KEY = "rate_limit:";
+
+    /**
+     * 验证码有效期(分钟)
+     */
+    public static final Integer CAPTCHA_EXPIRATION = 2;
+
+    /**
+     * 令牌
+     */
+    public static final String TOKEN = "token";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String TOKEN_PREFIX = "Bearer ";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String LOGIN_USER_KEY = "login_user_key";
+
+    /**
+     * 用户ID
+     */
+    public static final String JWT_USERID = "userid";
+
+    /**
+     * 用户名称
+     */
+    public static final String JWT_USERNAME = Claims.SUBJECT;
+
+    /**
+     * 用户头像
+     */
+    public static final String JWT_AVATAR = "avatar";
+
+    /**
+     * 创建时间
+     */
+    public static final String JWT_CREATED = "created";
+
+    /**
+     * 用户权限
+     */
+    public static final String JWT_AUTHORITIES = "authorities";
+
+    /**
+     * 参数管理 cache key
+     */
+    public static final String SYS_CONFIG_KEY = "sys_config:";
+
+    /**
+     * 字典管理 cache key
+     */
+    public static final String SYS_DICT_KEY = "sys_dict:";
+
+    /**
+     * 资源映射路径 前缀
+     */
+    public static final String RESOURCE_PREFIX = "/profile";
+
+    /**
+     * RMI 远程方法调用
+     */
+    public static final String LOOKUP_RMI = "rmi://";
+
+    /**
+     * LDAP 远程方法调用
+     */
+    public static final String LOOKUP_LDAP = "ldap://";
+}

+ 114 - 0
mp-common/src/main/java/com/qs/mp/common/constant/GenConstants.java

@@ -0,0 +1,114 @@
+package com.qs.mp.common.constant;
+
+/**
+ * 代码生成通用常量
+ * 
+ * @author ygp
+ */
+public class GenConstants
+{
+    /** 单表(增删改查) */
+    public static final String TPL_CRUD = "crud";
+
+    /** 树表(增删改查) */
+    public static final String TPL_TREE = "tree";
+
+    /** 主子表(增删改查) */
+    public static final String TPL_SUB = "sub";
+
+    /** 树编码字段 */
+    public static final String TREE_CODE = "treeCode";
+
+    /** 树父编码字段 */
+    public static final String TREE_PARENT_CODE = "treeParentCode";
+
+    /** 树名称字段 */
+    public static final String TREE_NAME = "treeName";
+
+    /** 上级菜单ID字段 */
+    public static final String PARENT_MENU_ID = "parentMenuId";
+
+    /** 上级菜单名称字段 */
+    public static final String PARENT_MENU_NAME = "parentMenuName";
+
+    /** 数据库字符串类型 */
+    public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" };
+
+    /** 数据库文本类型 */
+    public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" };
+
+    /** 数据库时间类型 */
+    public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" };
+
+    /** 数据库数字类型 */
+    public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer",
+            "bit", "bigint", "float", "double", "decimal" };
+
+    /** 页面不需要编辑字段 */
+    public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" };
+
+    /** 页面不需要显示的列表字段 */
+    public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by",
+            "update_time" };
+
+    /** 页面不需要查询字段 */
+    public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by",
+            "update_time", "remark" };
+
+    /** Entity基类字段 */
+    public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" };
+
+    /** Tree基类字段 */
+    public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" };
+
+    /** 文本框 */
+    public static final String HTML_INPUT = "input";
+
+    /** 文本域 */
+    public static final String HTML_TEXTAREA = "textarea";
+
+    /** 下拉框 */
+    public static final String HTML_SELECT = "select";
+
+    /** 单选框 */
+    public static final String HTML_RADIO = "radio";
+
+    /** 复选框 */
+    public static final String HTML_CHECKBOX = "checkbox";
+
+    /** 日期控件 */
+    public static final String HTML_DATETIME = "datetime";
+
+    /** 图片上传控件 */
+    public static final String HTML_IMAGE_UPLOAD = "imageUpload";
+
+    /** 文件上传控件 */
+    public static final String HTML_FILE_UPLOAD = "fileUpload";
+
+    /** 富文本控件 */
+    public static final String HTML_EDITOR = "editor";
+
+    /** 字符串类型 */
+    public static final String TYPE_STRING = "String";
+
+    /** 整型 */
+    public static final String TYPE_INTEGER = "Integer";
+
+    /** 长整型 */
+    public static final String TYPE_LONG = "Long";
+
+    /** 浮点型 */
+    public static final String TYPE_DOUBLE = "Double";
+
+    /** 高精度计算类型 */
+    public static final String TYPE_BIGDECIMAL = "BigDecimal";
+
+    /** 时间类型 */
+    public static final String TYPE_DATE = "Date";
+
+    /** 模糊查询 */
+    public static final String QUERY_LIKE = "LIKE";
+
+    /** 需要 */
+    public static final String REQUIRE = "1";
+}

+ 89 - 0
mp-common/src/main/java/com/qs/mp/common/constant/HttpStatus.java

@@ -0,0 +1,89 @@
+package com.qs.mp.common.constant;
+
+/**
+ * 返回状态码
+ * 
+ * @author ygp
+ */
+public class HttpStatus
+{
+    /**
+     * 操作成功
+     */
+    public static final int SUCCESS = 200;
+
+    /**
+     * 对象创建成功
+     */
+    public static final int CREATED = 201;
+
+    /**
+     * 请求已经被接受
+     */
+    public static final int ACCEPTED = 202;
+
+    /**
+     * 操作已经执行成功,但是没有返回数据
+     */
+    public static final int NO_CONTENT = 204;
+
+    /**
+     * 资源已被移除
+     */
+    public static final int MOVED_PERM = 301;
+
+    /**
+     * 重定向
+     */
+    public static final int SEE_OTHER = 303;
+
+    /**
+     * 资源没有被修改
+     */
+    public static final int NOT_MODIFIED = 304;
+
+    /**
+     * 参数列表错误(缺少,格式不匹配)
+     */
+    public static final int BAD_REQUEST = 400;
+
+    /**
+     * 未授权
+     */
+    public static final int UNAUTHORIZED = 401;
+
+    /**
+     * 访问受限,授权过期
+     */
+    public static final int FORBIDDEN = 403;
+
+    /**
+     * 资源,服务未找到
+     */
+    public static final int NOT_FOUND = 404;
+
+    /**
+     * 不允许的http方法
+     */
+    public static final int BAD_METHOD = 405;
+
+    /**
+     * 资源冲突,或者资源被锁
+     */
+    public static final int CONFLICT = 409;
+
+    /**
+     * 不支持的数据,媒体类型
+     */
+    public static final int UNSUPPORTED_TYPE = 415;
+
+    /**
+     * 系统内部错误
+     */
+    public static final int ERROR = 500;
+
+    /**
+     * 接口未实现
+     */
+    public static final int NOT_IMPLEMENTED = 501;
+}

+ 50 - 0
mp-common/src/main/java/com/qs/mp/common/constant/ScheduleConstants.java

@@ -0,0 +1,50 @@
+package com.qs.mp.common.constant;
+
+/**
+ * 任务调度通用常量
+ * 
+ * @author ygp
+ */
+public class ScheduleConstants
+{
+    public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
+
+    /** 执行目标key */
+    public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
+
+    /** 默认 */
+    public static final String MISFIRE_DEFAULT = "0";
+
+    /** 立即触发执行 */
+    public static final String MISFIRE_IGNORE_MISFIRES = "1";
+
+    /** 触发一次执行 */
+    public static final String MISFIRE_FIRE_AND_PROCEED = "2";
+
+    /** 不触发立即执行 */
+    public static final String MISFIRE_DO_NOTHING = "3";
+
+    public enum Status
+    {
+        /**
+         * 正常
+         */
+        NORMAL("0"),
+        /**
+         * 暂停
+         */
+        PAUSE("1");
+
+        private String value;
+
+        private Status(String value)
+        {
+            this.value = value;
+        }
+
+        public String getValue()
+        {
+            return value;
+        }
+    }
+}

+ 78 - 0
mp-common/src/main/java/com/qs/mp/common/constant/UserConstants.java

@@ -0,0 +1,78 @@
+package com.qs.mp.common.constant;
+
+/**
+ * 用户常量信息
+ * 
+ * @author ygp
+ */
+public class UserConstants
+{
+    /**
+     * 平台内系统用户的唯一标志
+     */
+    public static final String SYS_USER = "SYS_USER";
+
+    /** 正常状态 */
+    public static final String NORMAL = "0";
+
+    /** 异常状态 */
+    public static final String EXCEPTION = "1";
+
+    /** 用户封禁状态 */
+    public static final String USER_DISABLE = "1";
+
+    /** 角色封禁状态 */
+    public static final String ROLE_DISABLE = "1";
+
+    /** 部门正常状态 */
+    public static final String DEPT_NORMAL = "0";
+
+    /** 部门停用状态 */
+    public static final String DEPT_DISABLE = "1";
+
+    /** 字典正常状态 */
+    public static final String DICT_NORMAL = "0";
+
+    /** 是否为系统默认(是) */
+    public static final String YES = "Y";
+
+    /** 是否菜单外链(是) */
+    public static final String YES_FRAME = "0";
+
+    /** 是否菜单外链(否) */
+    public static final String NO_FRAME = "1";
+
+    /** 菜单类型(目录) */
+    public static final String TYPE_DIR = "M";
+
+    /** 菜单类型(菜单) */
+    public static final String TYPE_MENU = "C";
+
+    /** 菜单类型(按钮) */
+    public static final String TYPE_BUTTON = "F";
+
+    /** Layout组件标识 */
+    public final static String LAYOUT = "Layout";
+    
+    /** ParentView组件标识 */
+    public final static String PARENT_VIEW = "ParentView";
+
+    /** InnerLink组件标识 */
+    public final static String INNER_LINK = "InnerLink";
+
+    /** 校验返回结果码 */
+    public final static String UNIQUE = "0";
+    public final static String NOT_UNIQUE = "1";
+
+    /**
+     * 用户名长度限制
+     */
+    public static final int USERNAME_MIN_LENGTH = 2;
+    public static final int USERNAME_MAX_LENGTH = 20;
+
+    /**
+     * 密码长度限制
+     */
+    public static final int PASSWORD_MIN_LENGTH = 5;
+    public static final int PASSWORD_MAX_LENGTH = 20;
+}

+ 314 - 0
mp-common/src/main/java/com/qs/mp/common/core/domain/AjaxResult.java

@@ -0,0 +1,314 @@
+package com.qs.mp.common.core.domain;
+
+import java.util.HashMap;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 操作消息提醒
+ * 
+ * @author eepay
+ */
+public class AjaxResult extends HashMap<String, Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    public static final String CODE_TAG = "code";
+
+    public static final String MSG_TAG = "msg";
+
+    public static final String DATA_TAG = "data";
+
+    /**
+     * 状态类型
+     */
+    public enum Type
+    {
+        /** 成功 */
+        SUCCESS(0),
+        /** 警告 */
+        WARN(301),
+        /** 错误 */
+        ERROR(500),
+    	/** 登陆超时 */
+    	TIMEOUT(401);
+    	
+    	
+        private final int value;
+
+        Type(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
+
+    /** 状态类型 */
+    private Type type;
+
+    /** 状态码 */
+    private int code;
+
+    /** 返回内容 */
+    private String msg;
+
+    /** 数据对象 */
+    private Object data;
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+     */
+    public AjaxResult()
+    {
+    }
+    
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     * 
+     * @param type 状态类型
+     * @param msg 返回内容
+     */
+    public AjaxResult(Type type, String msg)
+    {
+        super.put(CODE_TAG, type.value);
+        super.put(MSG_TAG, msg);
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     * 
+     * @param type 状态类型
+     * @param msg 返回内容
+     * @param data 数据对象
+     */
+    public AjaxResult(Type type, String msg, Object data)
+    {
+        super.put(CODE_TAG, type.value);
+        super.put(MSG_TAG, msg);
+        super.put(DATA_TAG, data);
+    }
+    
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     * 
+     * @param code 状态码
+     * @param msg 返回内容
+     */
+    public AjaxResult(int code, String msg)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+    }
+
+    
+    /**
+     * 
+     * @param code
+     * @param msg
+     * @param data
+     */
+    public AjaxResult(int code, String msg, Object data)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+        super.put(DATA_TAG, data);
+    } 
+    
+    /**
+     * 返回成功消息
+     * 
+     * @return 成功消息
+     */
+    public static AjaxResult success()
+    {
+        return AjaxResult.success("操作成功");
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @param msg 返回内容
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg)
+    {
+        return AjaxResult.success(msg, null);
+    }
+    
+    /**
+     * 返回成功消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 成功消息
+     */
+    public static AjaxResult success(Object data)
+    {
+        return new AjaxResult(Type.SUCCESS, "", data);
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg, Object data)
+    {
+        return new AjaxResult(Type.SUCCESS, msg, data);
+    }
+
+    /**
+     * 返回警告消息
+     * 
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg)
+    {
+        return AjaxResult.warn(msg, null);
+    }
+
+    /**
+     * 返回警告消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg, Object data)
+    {
+        return new AjaxResult(Type.WARN, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @return
+     */
+    public static AjaxResult error()
+    {
+        return AjaxResult.error("操作失败");
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult error(String msg)
+    {
+        return AjaxResult.error(msg, null);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult error(String msg, Object data)
+    {
+        return new AjaxResult(Type.ERROR, msg, data);
+    }
+    
+    public static AjaxResult error(int code,String msg)
+    {
+        return new AjaxResult(code, msg);
+    }
+    
+    public static AjaxResult error(int code,String msg, Object data)
+    {
+        return new AjaxResult(code, msg, data);
+    }
+    
+    /**
+     * 返回错误消息
+     * 
+     * @return
+     */
+    public static AjaxResult timeout()
+    {
+        return AjaxResult.timeout("登陆超时");
+    }
+    
+    /**
+     * 返回警告消息
+     * 
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult timeout(String msg)
+    {
+        return AjaxResult.timeout(msg, null);
+    }
+
+    /**
+     * 返回警告消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult timeout(String msg, Object data)
+    {
+        return new AjaxResult(Type.TIMEOUT, msg, data);
+    }
+
+    public Type getType()
+    {
+        return type;
+    }
+
+    public void setType(Type type)
+    {
+        this.type = type;
+    }
+
+    public int getCode()
+    {
+        return code;
+    }
+
+    public void setCode(int code)
+    {
+        this.code = code;
+    }
+
+    public String getMsg()
+    {
+        return msg;
+    }
+
+    public void setMsg(String msg)
+    {
+        this.msg = msg;
+    }
+
+    public Object getData()
+    {
+        return data;
+    }
+
+    public void setData(Object data)
+    {
+        this.data = data;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("code", getCode())
+            .append("msg", getMsg())
+            .append("data", getData())
+            .toString();
+    }
+}

+ 114 - 0
mp-common/src/main/java/com/qs/mp/common/core/domain/BaseEntity.java

@@ -0,0 +1,114 @@
+package com.qs.mp.common.core.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * Entity基类
+ * 
+ * @author ygp
+ */
+public class BaseEntity implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 搜索值 */
+    private String searchValue;
+
+    /** 创建者 */
+    private String createBy;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 更新者 */
+    private String updateBy;
+
+    /** 更新时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
+    /** 备注 */
+    private String remark;
+
+    /** 请求参数 */
+    private Map<String, Object> params;
+
+    public String getSearchValue()
+    {
+        return searchValue;
+    }
+
+    public void setSearchValue(String searchValue)
+    {
+        this.searchValue = searchValue;
+    }
+
+    public String getCreateBy()
+    {
+        return createBy;
+    }
+
+    public void setCreateBy(String createBy)
+    {
+        this.createBy = createBy;
+    }
+
+    public Date getCreateTime()
+    {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime)
+    {
+        this.createTime = createTime;
+    }
+
+    public String getUpdateBy()
+    {
+        return updateBy;
+    }
+
+    public void setUpdateBy(String updateBy)
+    {
+        this.updateBy = updateBy;
+    }
+
+    public Date getUpdateTime()
+    {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime)
+    {
+        this.updateTime = updateTime;
+    }
+
+    public String getRemark()
+    {
+        return remark;
+    }
+
+    public void setRemark(String remark)
+    {
+        this.remark = remark;
+    }
+
+    public Map<String, Object> getParams()
+    {
+        if (params == null)
+        {
+            params = new HashMap<>();
+        }
+        return params;
+    }
+
+    public void setParams(Map<String, Object> params)
+    {
+        this.params = params;
+    }
+}

+ 79 - 0
mp-common/src/main/java/com/qs/mp/common/core/domain/TreeEntity.java

@@ -0,0 +1,79 @@
+package com.qs.mp.common.core.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tree基类
+ * 
+ * @author ygp
+ */
+public class TreeEntity extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 父菜单名称 */
+    private String parentName;
+
+    /** 父菜单ID */
+    private Long parentId;
+
+    /** 显示顺序 */
+    private Integer orderNum;
+
+    /** 祖级列表 */
+    private String ancestors;
+
+    /** 子部门 */
+    private List<?> children = new ArrayList<>();
+
+    public String getParentName()
+    {
+        return parentName;
+    }
+
+    public void setParentName(String parentName)
+    {
+        this.parentName = parentName;
+    }
+
+    public Long getParentId()
+    {
+        return parentId;
+    }
+
+    public void setParentId(Long parentId)
+    {
+        this.parentId = parentId;
+    }
+
+    public Integer getOrderNum()
+    {
+        return orderNum;
+    }
+
+    public void setOrderNum(Integer orderNum)
+    {
+        this.orderNum = orderNum;
+    }
+
+    public String getAncestors()
+    {
+        return ancestors;
+    }
+
+    public void setAncestors(String ancestors)
+    {
+        this.ancestors = ancestors;
+    }
+
+    public List<?> getChildren()
+    {
+        return children;
+    }
+
+    public void setChildren(List<?> children)
+    {
+        this.children = children;
+    }
+}

+ 82 - 0
mp-common/src/main/java/com/qs/mp/common/core/domain/model/LoginBody.java

@@ -0,0 +1,82 @@
+package com.qs.mp.common.core.domain.model;
+
+/**
+ * 用户登录对象
+ * 
+ * @author ygp
+ */
+public class LoginBody
+{
+    /**
+     * 用户名
+     */
+    private String username;
+
+    /**
+     * 用户密码
+     */
+    private String password;
+
+    /**
+     * 验证码
+     */
+    private String code;
+
+    /**
+     * 唯一标识
+     */
+    private String uuid = "";
+
+    /**
+     * 登录身份
+     */
+    private int identity = 0;
+
+    public String getUsername()
+    {
+        return username;
+    }
+
+    public void setUsername(String username)
+    {
+        this.username = username;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+
+    public String getCode()
+    {
+        return code;
+    }
+
+    public void setCode(String code)
+    {
+        this.code = code;
+    }
+
+    public String getUuid()
+    {
+        return uuid;
+    }
+
+    public void setUuid(String uuid)
+    {
+        this.uuid = uuid;
+    }
+
+    public int getIdentity() {
+        return identity;
+    }
+
+    public void setIdentity(int identity) {
+        this.identity = identity;
+    }
+}

+ 11 - 0
mp-common/src/main/java/com/qs/mp/common/core/domain/model/RegisterBody.java

@@ -0,0 +1,11 @@
+package com.qs.mp.common.core.domain.model;
+
+/**
+ * 用户注册对象
+ * 
+ * @author ygp
+ */
+public class RegisterBody extends LoginBody
+{
+
+}

+ 9 - 0
mp-common/src/main/java/com/qs/mp/common/core/mapper/BaseMapper.java

@@ -0,0 +1,9 @@
+package com.qs.mp.common.core.mapper;
+
+/**
+ * @author liugl
+ * 通用mapper
+ */
+
+public interface BaseMapper<T> extends com.baomidou.mybatisplus.core.mapper.BaseMapper<T> {
+}

+ 50 - 0
mp-common/src/main/java/com/qs/mp/common/core/mapper/BaseMetaObjectHandler.java

@@ -0,0 +1,50 @@
+package com.qs.mp.common.core.mapper;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * @author liugl
+ * mybatisPlus的自动注入
+ */
+@Component
+public class BaseMetaObjectHandler implements MetaObjectHandler {
+
+    public static final String FIELD_CREATE_TIME = "createTime";
+    public static final String FIELD_UPDATE_TIME = "updateTime";
+    public static final String FIELD_CREATE_USER = "createUserId";
+
+    /**
+     * 插入元对象字段填充(用于插入时对公共字段的填充)
+     *
+     * @param metaObject 元对象
+     */
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        Date date = new Date();
+        this.strictInsertFill(metaObject, FIELD_CREATE_TIME, Date.class, date);
+//        if (UserUtil.getUser() != null) {
+//            this.strictInsertFill(metaObject, FIELD_CREATE_USER, Long.class, UserUtil.getUserId());
+//        }
+    }
+
+
+    @Override
+    public MetaObjectHandler fillStrategy(MetaObject metaObject, String fieldName, Object fieldVal) {
+        setFieldValByName(fieldName, fieldVal, metaObject);
+        return this;
+    }
+
+    /**
+     * 更新元对象字段填充(用于更新时对公共字段的填充)
+     *
+     * @param metaObject 元对象
+     */
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        this.strictUpdateFill(metaObject, FIELD_UPDATE_TIME, Date.class, new Date());
+    }
+}

+ 84 - 0
mp-common/src/main/java/com/qs/mp/common/core/page/PageDomain.java

@@ -0,0 +1,84 @@
+package com.qs.mp.common.core.page;
+
+import com.qs.mp.common.utils.StringUtils;
+
+/**
+ * 分页数据
+ * 
+ * @author ygp
+ */
+public class PageDomain
+{
+    /** 当前记录起始索引 */
+    private Integer pageNum;
+
+    /** 每页显示记录数 */
+    private Integer pageSize;
+
+    /** 排序列 */
+    private String orderByColumn;
+
+    /** 排序的方向desc或者asc */
+    private String isAsc = "asc";
+
+    public String getOrderBy()
+    {
+        if (StringUtils.isEmpty(orderByColumn))
+        {
+            return "";
+        }
+        return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
+    }
+
+    public Integer getPageNum()
+    {
+        return pageNum;
+    }
+
+    public void setPageNum(Integer pageNum)
+    {
+        this.pageNum = pageNum;
+    }
+
+    public Integer getPageSize()
+    {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize)
+    {
+        this.pageSize = pageSize;
+    }
+
+    public String getOrderByColumn()
+    {
+        return orderByColumn;
+    }
+
+    public void setOrderByColumn(String orderByColumn)
+    {
+        this.orderByColumn = orderByColumn;
+    }
+
+    public String getIsAsc()
+    {
+        return isAsc;
+    }
+
+    public void setIsAsc(String isAsc)
+    {
+        if (StringUtils.isNotEmpty(isAsc))
+        {
+            // 兼容前端排序类型
+            if ("ascending".equals(isAsc))
+            {
+                isAsc = "asc";
+            }
+            else if ("descending".equals(isAsc))
+            {
+                isAsc = "desc";
+            }
+            this.isAsc = isAsc;
+        }
+    }
+}

+ 85 - 0
mp-common/src/main/java/com/qs/mp/common/core/page/TableDataInfo.java

@@ -0,0 +1,85 @@
+package com.qs.mp.common.core.page;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 表格分页数据对象
+ * 
+ * @author ygp
+ */
+public class TableDataInfo implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 总记录数 */
+    private long total;
+
+    /** 列表数据 */
+    private List<?> rows;
+
+    /** 消息状态码 */
+    private int code;
+
+    /** 消息内容 */
+    private String msg;
+
+    /**
+     * 表格数据对象
+     */
+    public TableDataInfo()
+    {
+    }
+
+    /**
+     * 分页
+     * 
+     * @param list 列表数据
+     * @param total 总记录数
+     */
+    public TableDataInfo(List<?> list, int total)
+    {
+        this.rows = list;
+        this.total = total;
+    }
+
+    public long getTotal()
+    {
+        return total;
+    }
+
+    public void setTotal(long total)
+    {
+        this.total = total;
+    }
+
+    public List<?> getRows()
+    {
+        return rows;
+    }
+
+    public void setRows(List<?> rows)
+    {
+        this.rows = rows;
+    }
+
+    public int getCode()
+    {
+        return code;
+    }
+
+    public void setCode(int code)
+    {
+        this.code = code;
+    }
+
+    public String getMsg()
+    {
+        return msg;
+    }
+
+    public void setMsg(String msg)
+    {
+        this.msg = msg;
+    }
+}

+ 49 - 0
mp-common/src/main/java/com/qs/mp/common/core/page/TableSupport.java

@@ -0,0 +1,49 @@
+package com.qs.mp.common.core.page;
+
+import com.qs.mp.common.utils.ServletUtils;
+
+/**
+ * 表格数据处理
+ * 
+ * @author ygp
+ */
+public class TableSupport
+{
+    /**
+     * 当前记录起始索引
+     */
+    public static final String PAGE_NUM = "pageNum";
+
+    /**
+     * 每页显示记录数
+     */
+    public static final String PAGE_SIZE = "pageSize";
+
+    /**
+     * 排序列
+     */
+    public static final String ORDER_BY_COLUMN = "orderByColumn";
+
+    /**
+     * 排序的方向 "desc" 或者 "asc".
+     */
+    public static final String IS_ASC = "isAsc";
+
+    /**
+     * 封装分页对象
+     */
+    public static PageDomain getPageDomain()
+    {
+        PageDomain pageDomain = new PageDomain();
+        pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM));
+        pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE));
+        pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
+        pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
+        return pageDomain;
+    }
+
+    public static PageDomain buildPageRequest()
+    {
+        return getPageDomain();
+    }
+}

+ 234 - 0
mp-common/src/main/java/com/qs/mp/common/core/redis/RedisCache.java

@@ -0,0 +1,234 @@
+package com.qs.mp.common.core.redis;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundSetOperations;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Component;
+
+/**
+ * spring redis 工具类
+ *
+ * @author ygp
+ **/
+@SuppressWarnings(value = { "unchecked", "rawtypes" })
+@Component
+public class RedisCache
+{
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    /**
+     * 缓存基本的对象,Integer、String、实体类等
+     *
+     * @param key 缓存的键值
+     * @param value 缓存的值
+     */
+    public <T> void setCacheObject(final String key, final T value)
+    {
+        redisTemplate.opsForValue().set(key, value);
+    }
+
+    /**
+     * 缓存基本的对象,Integer、String、实体类等
+     *
+     * @param key 缓存的键值
+     * @param value 缓存的值
+     * @param timeout 时间
+     * @param timeUnit 时间颗粒度
+     */
+    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
+    {
+        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
+    }
+
+    /**
+     * 设置有效时间
+     *
+     * @param key Redis键
+     * @param timeout 超时时间
+     * @return true=设置成功;false=设置失败
+     */
+    public boolean expire(final String key, final long timeout)
+    {
+        return expire(key, timeout, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 设置有效时间
+     *
+     * @param key Redis键
+     * @param timeout 超时时间
+     * @param unit 时间单位
+     * @return true=设置成功;false=设置失败
+     */
+    public boolean expire(final String key, final long timeout, final TimeUnit unit)
+    {
+        return redisTemplate.expire(key, timeout, unit);
+    }
+
+    /**
+     * 获得缓存的基本对象。
+     *
+     * @param key 缓存键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> T getCacheObject(final String key)
+    {
+        ValueOperations<String, T> operation = redisTemplate.opsForValue();
+        return operation.get(key);
+    }
+
+    /**
+     * 删除单个对象
+     *
+     * @param key
+     */
+    public boolean deleteObject(final String key)
+    {
+        return redisTemplate.delete(key);
+    }
+
+    /**
+     * 删除集合对象
+     *
+     * @param collection 多个对象
+     * @return
+     */
+    public long deleteObject(final Collection collection)
+    {
+        return redisTemplate.delete(collection);
+    }
+
+    /**
+     * 缓存List数据
+     *
+     * @param key 缓存的键值
+     * @param dataList 待缓存的List数据
+     * @return 缓存的对象
+     */
+    public <T> long setCacheList(final String key, final List<T> dataList)
+    {
+        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
+        return count == null ? 0 : count;
+    }
+
+    /**
+     * 获得缓存的list对象
+     *
+     * @param key 缓存的键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> List<T> getCacheList(final String key)
+    {
+        return redisTemplate.opsForList().range(key, 0, -1);
+    }
+
+    /**
+     * 缓存Set
+     *
+     * @param key 缓存键值
+     * @param dataSet 缓存的数据
+     * @return 缓存数据的对象
+     */
+    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
+    {
+        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
+        Iterator<T> it = dataSet.iterator();
+        while (it.hasNext())
+        {
+            setOperation.add(it.next());
+        }
+        return setOperation;
+    }
+
+    /**
+     * 获得缓存的set
+     *
+     * @param key
+     * @return
+     */
+    public <T> Set<T> getCacheSet(final String key)
+    {
+        return redisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 缓存Map
+     *
+     * @param key
+     * @param dataMap
+     */
+    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
+    {
+        if (dataMap != null) {
+            redisTemplate.opsForHash().putAll(key, dataMap);
+        }
+    }
+
+    /**
+     * 获得缓存的Map
+     *
+     * @param key
+     * @return
+     */
+    public <T> Map<String, T> getCacheMap(final String key)
+    {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * 往Hash中存入数据
+     *
+     * @param key Redis键
+     * @param hKey Hash键
+     * @param value 值
+     */
+    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
+    {
+        redisTemplate.opsForHash().put(key, hKey, value);
+    }
+
+    /**
+     * 获取Hash中的数据
+     *
+     * @param key Redis键
+     * @param hKey Hash键
+     * @return Hash中的对象
+     */
+    public <T> T getCacheMapValue(final String key, final String hKey)
+    {
+        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
+        return opsForHash.get(key, hKey);
+    }
+
+    /**
+     * 获取多个Hash中的数据
+     *
+     * @param key Redis键
+     * @param hKeys Hash键集合
+     * @return Hash对象集合
+     */
+    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
+    {
+        return redisTemplate.opsForHash().multiGet(key, hKeys);
+    }
+
+    /**
+     * 获得缓存的基本对象列表
+     *
+     * @param pattern 字符串前缀
+     * @return 对象列表
+     */
+    public Collection<String> keys(final String pattern)
+    {
+        return redisTemplate.keys(pattern);
+    }
+}

+ 12 - 0
mp-common/src/main/java/com/qs/mp/common/core/service/BaseService.java

@@ -0,0 +1,12 @@
+package com.qs.mp.common.core.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * 通用service
+ * @author liugl
+ * @param <T> model
+ */
+public interface BaseService<T> extends IService<T> {
+
+}

+ 231 - 0
mp-common/src/main/java/com/qs/mp/common/core/service/BaseServiceImpl.java

@@ -0,0 +1,231 @@
+package com.qs.mp.common.core.service;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
+import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
+import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+import com.qs.mp.common.core.mapper.BaseMapper;
+import com.qs.mp.common.core.mapper.BaseMetaObjectHandler;
+
+import org.apache.ibatis.reflection.ExceptionUtil;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.MyBatisExceptionTranslator;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.lang.reflect.Field;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author liugl
+ * 通用serviceImpl
+ */
+public class BaseServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {
+
+
+    @Override
+    @Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
+    public boolean saveBatch(Collection<T> entityList) {
+        return saveBatch(entityList, 1000);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
+    public boolean saveBatch(Collection<T> entityList, int batchSize) {
+        if (CollUtil.isEmpty(entityList)) {
+            return true;
+        }
+        T model = entityList.iterator().next();
+        Class<?> tClass = model.getClass();
+        TableInfo table = SqlHelper.table(tClass);
+        insertFill(entityList,table);
+        List<Field> allFields = TableInfoHelper.getAllFields(tClass);
+        Set<String> fieldSet = allFields.stream()
+                .filter(field -> {
+                            TableId tableId = field.getAnnotation(TableId.class);
+                            return tableId == null || !Objects.equals(tableId.type(),IdType.AUTO);
+                        })
+                .map(field -> StrUtil.toUnderlineCase(field.getName())).collect(Collectors.toSet());
+        Map<String, Object> attrs = BeanUtil.beanToMap(model, true, true);
+        int index = 0;
+        StringBuilder columns = new StringBuilder();
+        for (Map.Entry<String, Object> e : attrs.entrySet()) {
+            if (fieldSet.contains(e.getKey())){
+                if (index++ > 0) {
+                    columns.append(',');
+                }
+                columns.append(e.getKey());
+            }
+        }
+        StringBuilder sql = new StringBuilder();
+        List<Object> parasNoUse = new ArrayList<>();
+
+        forModelSave(table, attrs, sql, parasNoUse);
+        int[] result = batch(tClass,sql.toString(), columns.toString(), entityList, batchSize);
+        return result.length > 0;
+    }
+
+    /**
+     * 字段填充
+     */
+    private void insertFill(Collection<T> entityList,TableInfo tableInfo) {
+        final IdentifierGenerator identifierGenerator = GlobalConfigUtils.getGlobalConfig(tableInfo.getConfiguration()).getIdentifierGenerator();
+        for (T model : entityList) {
+            Map<String, Object> attrs = BeanUtil.beanToMap(model);
+            Object obj = attrs.get(tableInfo.getKeyColumn());
+            if(obj == null) {
+                if (tableInfo.getIdType().getKey() == IdType.ASSIGN_ID.getKey()) {
+                    if (Number.class.isAssignableFrom(tableInfo.getKeyType())) {
+                        attrs.put(tableInfo.getKeyColumn(), identifierGenerator.nextId(model));
+                    } else {
+                        attrs.put(tableInfo.getKeyColumn(), identifierGenerator.nextId(model).toString());
+                    }
+                } else if (tableInfo.getIdType().getKey() == IdType.ASSIGN_UUID.getKey()) {
+                    attrs.put(tableInfo.getKeyColumn(), identifierGenerator.nextUUID(model));
+                }
+            }
+            for (String key : attrs.keySet()) {
+                if (attrs.get(key) == null) {
+                    switch (key) {
+                        case BaseMetaObjectHandler.FIELD_CREATE_TIME:
+                        case BaseMetaObjectHandler.FIELD_UPDATE_TIME:
+                            attrs.put(key, DateUtil.formatDateTime(new Date()));
+                            break;
+//                        case BaseMetaObjectHandler.FIELD_CREATE_USER:
+//                            attrs.put(key, UserUtil.getUserId());
+//                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+            BeanUtil.copyProperties(attrs, model);
+        }
+    }
+
+    /**
+     * 拼接sql
+     */
+    private void forModelSave(TableInfo table, Map<String, Object> attrs, StringBuilder sql, List<Object> paras) {
+        sql.append("insert into `").append(table.getTableName()).append("`(");
+        Set<String> columnTypeSet = table.getFieldList().stream().map(TableFieldInfo::getColumn).collect(Collectors.toSet());
+        if(!table.getIdType().equals(IdType.AUTO)){
+            columnTypeSet.add(table.getKeyColumn());
+        }
+        CollUtil.newArrayList(table.getAllSqlSelect().split(","));
+        StringBuilder temp = new StringBuilder(") values(");
+        for (Map.Entry<String, Object> e : attrs.entrySet()) {
+            String colName = e.getKey();
+            if (columnTypeSet.contains(colName)) {
+                if (paras.size() > 0) {
+                    sql.append(", ");
+                    temp.append(", ");
+                }
+                sql.append('`').append(colName).append('`');
+                temp.append('?');
+                paras.add(e.getValue());
+            }
+        }
+        sql.append(temp.toString()).append(')');
+    }
+
+    /**
+     * 批量保存
+     */
+    private int[] batch(Class<?> tClass,String sql, String columns, Collection<T> entityList, int batchSize) {
+        int[] batch;
+        Connection conn = null;
+        SqlSessionFactory sqlSessionFactory = SqlHelper.sqlSessionFactory(tClass);
+        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
+        try {
+            conn = sqlSession.getConnection();
+            batch = batch(conn, sql, columns, new ArrayList<>(entityList), batchSize);
+            sqlSession.commit();
+        } catch (Throwable t) {
+            t.printStackTrace();
+            sqlSession.rollback();
+            Throwable unwrapped = ExceptionUtil.unwrapThrowable(t);
+            if (unwrapped instanceof RuntimeException) {
+                MyBatisExceptionTranslator myBatisExceptionTranslator
+                        = new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true);
+                throw Objects.requireNonNull(myBatisExceptionTranslator.translateExceptionIfPossible((RuntimeException) unwrapped));
+            }
+            throw ExceptionUtils.mpe(unwrapped);
+        } finally {
+            sqlSession.close();
+        }
+        return batch;
+    }
+
+    private int[] batch(Connection conn, String sql, String columns, List<T> list, int batchSize) throws SQLException {
+        if (list == null || list.size() == 0) {
+            return new int[0];
+        }
+        if (batchSize < 1) {
+            throw new IllegalArgumentException("The batchSize must more than 0.");
+        }
+        String[] columnArray = columns.split(",");
+        for (int i = 0; i < columnArray.length; i++) {
+            columnArray[i] = columnArray[i].trim();
+        }
+        int counter = 0;
+        int pointer = 0;
+        int size = list.size();
+        int[] result = new int[size];
+        PreparedStatement pst = conn.prepareStatement(sql);
+        for (int i = 0; i < size; i++) {
+            Map map = BeanUtil.beanToMap(list.get(i), true, true);
+            for (int j = 0; j < columnArray.length; j++) {
+                Object value = map.get(columnArray[j]);
+                if (value instanceof Date) {
+                    if (value instanceof java.sql.Date) {
+                        pst.setDate(j + 1, (java.sql.Date) value);
+                    } else if (value instanceof java.sql.Timestamp) {
+                        pst.setTimestamp(j + 1, (java.sql.Timestamp) value);
+                    } else {
+                        Date d = (Date) value;
+                        pst.setTimestamp(j + 1, new java.sql.Timestamp(d.getTime()));
+                    }
+                } else {
+                    pst.setObject(j + 1, value);
+                }
+            }
+            pst.addBatch();
+            if (++counter >= batchSize) {
+                counter = 0;
+                int[] r = pst.executeBatch();
+                for (int k = 0; k < r.length; k++) {
+                    result[pointer++] = r[k];
+                }
+            }
+        }
+        if (counter != 0) {
+            int[] r = pst.executeBatch();
+            for (int k = 0; k < r.length; k++) {
+                result[pointer++] = r[k];
+            }
+        }
+        try {
+            pst.close();
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+        return result;
+    }
+}

+ 30 - 0
mp-common/src/main/java/com/qs/mp/common/core/sign/SignParam.java

@@ -0,0 +1,30 @@
+package com.qs.mp.common.core.sign;
+
+public class SignParam {
+
+    private String appKey; //client key
+    private String sign;
+    private String timestamp;   //时间长整型
+    
+    
+    public String getAppKey() {
+        return appKey;
+    }
+    public void setAppKey(String appKey) {
+        this.appKey = appKey;
+    }
+    public String getSign() {
+        return sign;
+    }
+    public void setSign(String sign) {
+        this.sign = sign;
+    }
+    public String getTimestamp() {
+        return timestamp;
+    }
+    public void setTimestamp(String timestamp) {
+        this.timestamp = timestamp;
+    }
+    
+    
+}

+ 86 - 0
mp-common/src/main/java/com/qs/mp/common/core/text/CharsetKit.java

@@ -0,0 +1,86 @@
+package com.qs.mp.common.core.text;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import com.qs.mp.common.utils.StringUtils;
+
+/**
+ * 字符集工具类
+ * 
+ * @author ygp
+ */
+public class CharsetKit
+{
+    /** ISO-8859-1 */
+    public static final String ISO_8859_1 = "ISO-8859-1";
+    /** UTF-8 */
+    public static final String UTF_8 = "UTF-8";
+    /** GBK */
+    public static final String GBK = "GBK";
+
+    /** ISO-8859-1 */
+    public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
+    /** UTF-8 */
+    public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
+    /** GBK */
+    public static final Charset CHARSET_GBK = Charset.forName(GBK);
+
+    /**
+     * 转换为Charset对象
+     * 
+     * @param charset 字符集,为空则返回默认字符集
+     * @return Charset
+     */
+    public static Charset charset(String charset)
+    {
+        return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     * 
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, String srcCharset, String destCharset)
+    {
+        return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     * 
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, Charset srcCharset, Charset destCharset)
+    {
+        if (null == srcCharset)
+        {
+            srcCharset = StandardCharsets.ISO_8859_1;
+        }
+
+        if (null == destCharset)
+        {
+            destCharset = StandardCharsets.UTF_8;
+        }
+
+        if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
+        {
+            return source;
+        }
+        return new String(source.getBytes(srcCharset), destCharset);
+    }
+
+    /**
+     * @return 系统字符集编码
+     */
+    public static String systemCharset()
+    {
+        return Charset.defaultCharset().name();
+    }
+}

+ 1005 - 0
mp-common/src/main/java/com/qs/mp/common/core/text/Convert.java

@@ -0,0 +1,1005 @@
+package com.qs.mp.common.core.text;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.text.NumberFormat;
+import java.util.Set;
+import com.qs.mp.common.utils.StringUtils;
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * 类型转换器
+ *
+ * @author ygp
+ */
+public class Convert
+{
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static String toStr(Object value, String defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof String)
+        {
+            return (String) value;
+        }
+        return value.toString();
+    }
+
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static String toStr(Object value)
+    {
+        return toStr(value, null);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Character toChar(Object value, Character defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Character)
+        {
+            return (Character) value;
+        }
+
+        final String valueStr = toStr(value, null);
+        return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Character toChar(Object value)
+    {
+        return toChar(value, null);
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Byte toByte(Object value, Byte defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Byte)
+        {
+            return (Byte) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).byteValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Byte.parseByte(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Byte toByte(Object value)
+    {
+        return toByte(value, null);
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Short toShort(Object value, Short defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Short)
+        {
+            return (Short) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).shortValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Short.parseShort(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Short toShort(Object value)
+    {
+        return toShort(value, null);
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Number toNumber(Object value, Number defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Number)
+        {
+            return (Number) value;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return NumberFormat.getInstance().parse(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Number toNumber(Object value)
+    {
+        return toNumber(value, null);
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Integer toInt(Object value, Integer defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Integer)
+        {
+            return (Integer) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).intValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Integer.parseInt(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Integer toInt(Object value)
+    {
+        return toInt(value, null);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     *
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String str)
+    {
+        return toIntArray(",", str);
+    }
+
+    /**
+     * 转换为Long数组<br>
+     *
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String str)
+    {
+        return toLongArray(",", str);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     *
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Integer[] {};
+        }
+        String[] arr = str.split(split);
+        final Integer[] ints = new Integer[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Integer v = toInt(arr[i], 0);
+            ints[i] = v;
+        }
+        return ints;
+    }
+
+    /**
+     * 转换为Long数组<br>
+     *
+     * @param split 分隔符
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Long[] {};
+        }
+        String[] arr = str.split(split);
+        final Long[] longs = new Long[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Long v = toLong(arr[i], null);
+            longs[i] = v;
+        }
+        return longs;
+    }
+
+    /**
+     * 转换为String数组<br>
+     *
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String str)
+    {
+        return toStrArray(",", str);
+    }
+
+    /**
+     * 转换为String数组<br>
+     *
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String split, String str)
+    {
+        return str.split(split);
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Long toLong(Object value, Long defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Long)
+        {
+            return (Long) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).longValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).longValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Long toLong(Object value)
+    {
+        return toLong(value, null);
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Double toDouble(Object value, Double defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Double)
+        {
+            return (Double) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).doubleValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).doubleValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Double toDouble(Object value)
+    {
+        return toDouble(value, null);
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Float toFloat(Object value, Float defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Float)
+        {
+            return (Float) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).floatValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Float.parseFloat(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Float toFloat(Object value)
+    {
+        return toFloat(value, null);
+    }
+
+    /**
+     * 转换为boolean<br>
+     * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value, Boolean defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Boolean)
+        {
+            return (Boolean) value;
+        }
+        String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        valueStr = valueStr.trim().toLowerCase();
+        switch (valueStr)
+        {
+            case "true":
+                return true;
+            case "false":
+                return false;
+            case "yes":
+                return true;
+            case "ok":
+                return true;
+            case "no":
+                return false;
+            case "1":
+                return true;
+            case "0":
+                return false;
+            default:
+                return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为boolean<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value)
+    {
+        return toBool(value, null);
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     *
+     * @param clazz Enum的Class
+     * @param value 值
+     * @param defaultValue 默认值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (clazz.isAssignableFrom(value.getClass()))
+        {
+            @SuppressWarnings("unchecked")
+            E myE = (E) value;
+            return myE;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Enum.valueOf(clazz, valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     *
+     * @param clazz Enum的Class
+     * @param value 值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value)
+    {
+        return toEnum(clazz, value, null);
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value, BigInteger defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigInteger)
+        {
+            return (BigInteger) value;
+        }
+        if (value instanceof Long)
+        {
+            return BigInteger.valueOf((Long) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigInteger(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value)
+    {
+        return toBigInteger(value, null);
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigDecimal)
+        {
+            return (BigDecimal) value;
+        }
+        if (value instanceof Long)
+        {
+            return new BigDecimal((Long) value);
+        }
+        if (value instanceof Double)
+        {
+            return new BigDecimal((Double) value);
+        }
+        if (value instanceof Integer)
+        {
+            return new BigDecimal((Integer) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigDecimal(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value)
+    {
+        return toBigDecimal(value, null);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     *
+     * @param obj 对象
+     * @return 字符串
+     */
+    public static String utf8Str(Object obj)
+    {
+        return str(obj, CharsetKit.CHARSET_UTF_8);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     *
+     * @param obj 对象
+     * @param charsetName 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, String charsetName)
+    {
+        return str(obj, Charset.forName(charsetName));
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     *
+     * @param obj 对象
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, Charset charset)
+    {
+        if (null == obj)
+        {
+            return null;
+        }
+
+        if (obj instanceof String)
+        {
+            return (String) obj;
+        }
+        else if (obj instanceof byte[])
+        {
+            return str((byte[]) obj, charset);
+        }
+        else if (obj instanceof Byte[])
+        {
+            byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
+            return str(bytes, charset);
+        }
+        else if (obj instanceof ByteBuffer)
+        {
+            return str((ByteBuffer) obj, charset);
+        }
+        return obj.toString();
+    }
+
+    /**
+     * 将byte数组转为字符串
+     *
+     * @param bytes byte数组
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(byte[] bytes, String charset)
+    {
+        return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
+    }
+
+    /**
+     * 解码字节码
+     *
+     * @param data 字符串
+     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+     * @return 解码后的字符串
+     */
+    public static String str(byte[] data, Charset charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        if (null == charset)
+        {
+            return new String(data);
+        }
+        return new String(data, charset);
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     *
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, String charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        return str(data, Charset.forName(charset));
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     *
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, Charset charset)
+    {
+        if (null == charset)
+        {
+            charset = Charset.defaultCharset();
+        }
+        return charset.decode(data).toString();
+    }
+
+    // ----------------------------------------------------------------------- 全角半角转换
+    /**
+     * 半角转全角
+     *
+     * @param input String.
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input)
+    {
+        return toSBC(input, null);
+    }
+
+    /**
+     * 半角转全角
+     *
+     * @param input String
+     * @param notConvertSet 不替换的字符集合
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input, Set<Character> notConvertSet)
+    {
+        char c[] = input.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == ' ')
+            {
+                c[i] = '\u3000';
+            }
+            else if (c[i] < '\177')
+            {
+                c[i] = (char) (c[i] + 65248);
+
+            }
+        }
+        return new String(c);
+    }
+
+    /**
+     * 全角转半角
+     *
+     * @param input String.
+     * @return 半角字符串
+     */
+    public static String toDBC(String input)
+    {
+        return toDBC(input, null);
+    }
+
+    /**
+     * 替换全角为半角
+     *
+     * @param text 文本
+     * @param notConvertSet 不替换的字符集合
+     * @return 替换后的字符
+     */
+    public static String toDBC(String text, Set<Character> notConvertSet)
+    {
+        char c[] = text.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == '\u3000')
+            {
+                c[i] = ' ';
+            }
+            else if (c[i] > '\uFF00' && c[i] < '\uFF5F')
+            {
+                c[i] = (char) (c[i] - 65248);
+            }
+        }
+        String returnString = new String(c);
+
+        return returnString;
+    }
+
+    /**
+     * 数字金额大写转换 先写个完整的然后将如零拾替换成零
+     *
+     * @param n 数字
+     * @return 中文大写数字
+     */
+    public static String digitUppercase(double n)
+    {
+        String[] fraction = { "角", "分" };
+        String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
+        String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } };
+
+        String head = n < 0 ? "负" : "";
+        n = Math.abs(n);
+
+        String s = "";
+        for (int i = 0; i < fraction.length; i++)
+        {
+            s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
+        }
+        if (s.length() < 1)
+        {
+            s = "整";
+        }
+        int integerPart = (int) Math.floor(n);
+
+        for (int i = 0; i < unit[0].length && integerPart > 0; i++)
+        {
+            String p = "";
+            for (int j = 0; j < unit[1].length && n > 0; j++)
+            {
+                p = digit[integerPart % 10] + unit[1][j] + p;
+                integerPart = integerPart / 10;
+            }
+            s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
+        }
+        return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
+    }
+}

+ 92 - 0
mp-common/src/main/java/com/qs/mp/common/core/text/StrFormatter.java

@@ -0,0 +1,92 @@
+package com.qs.mp.common.core.text;
+
+import com.qs.mp.common.utils.StringUtils;
+
+/**
+ * 字符串格式化
+ * 
+ * @author ygp
+ */
+public class StrFormatter
+{
+    public static final String EMPTY_JSON = "{}";
+    public static final char C_BACKSLASH = '\\';
+    public static final char C_DELIM_START = '{';
+    public static final char C_DELIM_END = '}';
+
+    /**
+     * 格式化字符串<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     * 
+     * @param strPattern 字符串模板
+     * @param argArray 参数列表
+     * @return 结果
+     */
+    public static String format(final String strPattern, final Object... argArray)
+    {
+        if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray))
+        {
+            return strPattern;
+        }
+        final int strPatternLength = strPattern.length();
+
+        // 初始化定义好的长度以获得更好的性能
+        StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
+
+        int handledPosition = 0;
+        int delimIndex;// 占位符所在位置
+        for (int argIndex = 0; argIndex < argArray.length; argIndex++)
+        {
+            delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
+            if (delimIndex == -1)
+            {
+                if (handledPosition == 0)
+                {
+                    return strPattern;
+                }
+                else
+                { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
+                    sbuf.append(strPattern, handledPosition, strPatternLength);
+                    return sbuf.toString();
+                }
+            }
+            else
+            {
+                if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH)
+                {
+                    if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH)
+                    {
+                        // 转义符之前还有一个转义符,占位符依旧有效
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                        handledPosition = delimIndex + 2;
+                    }
+                    else
+                    {
+                        // 占位符被转义
+                        argIndex--;
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(C_DELIM_START);
+                        handledPosition = delimIndex + 1;
+                    }
+                }
+                else
+                {
+                    // 正常占位符
+                    sbuf.append(strPattern, handledPosition, delimIndex);
+                    sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                    handledPosition = delimIndex + 2;
+                }
+            }
+        }
+        // 加入最后一个占位符后所有的字符
+        sbuf.append(strPattern, handledPosition, strPattern.length());
+
+        return sbuf.toString();
+    }
+}

+ 20 - 0
mp-common/src/main/java/com/qs/mp/common/enums/BusinessStatus.java

@@ -0,0 +1,20 @@
+package com.qs.mp.common.enums;
+
+/**
+ * 操作状态
+ * 
+ * @author ygp
+ *
+ */
+public enum BusinessStatus
+{
+    /**
+     * 成功
+     */
+    SUCCESS,
+
+    /**
+     * 失败
+     */
+    FAIL,
+}

+ 59 - 0
mp-common/src/main/java/com/qs/mp/common/enums/BusinessType.java

@@ -0,0 +1,59 @@
+package com.qs.mp.common.enums;
+
+/**
+ * 业务操作类型
+ * 
+ * @author ygp
+ */
+public enum BusinessType
+{
+    /**
+     * 其它
+     */
+    OTHER,
+
+    /**
+     * 新增
+     */
+    INSERT,
+
+    /**
+     * 修改
+     */
+    UPDATE,
+
+    /**
+     * 删除
+     */
+    DELETE,
+
+    /**
+     * 授权
+     */
+    GRANT,
+
+    /**
+     * 导出
+     */
+    EXPORT,
+
+    /**
+     * 导入
+     */
+    IMPORT,
+
+    /**
+     * 强退
+     */
+    FORCE,
+
+    /**
+     * 生成代码
+     */
+    GENCODE,
+    
+    /**
+     * 清空数据
+     */
+    CLEAN,
+}

+ 19 - 0
mp-common/src/main/java/com/qs/mp/common/enums/DataSourceType.java

@@ -0,0 +1,19 @@
+package com.qs.mp.common.enums;
+
+/**
+ * 数据源
+ * 
+ * @author ygp
+ */
+public enum DataSourceType
+{
+    /**
+     * 主库
+     */
+    MASTER,
+
+    /**
+     * 从库
+     */
+    SLAVE
+}

+ 51 - 0
mp-common/src/main/java/com/qs/mp/common/enums/ErrorCodeEnum.java

@@ -0,0 +1,51 @@
+package com.qs.mp.common.enums;
+
+/**
+ * @auther duota
+ * @create 2021 2021/9/4 2:59 下午
+ * @describe
+ */
+public enum ErrorCodeEnum {
+
+  ERROR_CODE_1000(1000, "数据访问错误"),
+  ERROR_CODE_1001(1001, "请求参数为空"),
+  ERROR_CODE_1002(1002, "用户登录信息为空"),
+  ERROR_CODE_1004(1004, "获取锁失败"),
+  ERROR_CODE_1005(1005, "用户微信未授权登录"),
+  ERROR_CODE_1006(1006, "客户ID不存在"),
+  ERROR_CODE_1007(1007, "收支类型错误"),
+  ERROR_CODE_1008(1008, "余额不足,结算失败"),
+  ERROR_CODE_1009(1009, "数据更新异常"),
+  ERROR_CODE_1010(1010, "数据对象不存在或者返回重复记录"),
+  ERROR_CODE_1011(1011, "请求参数错误"),
+  ERROR_CODE_1012(1012, "查询微信用户信息失败"),
+  ERROR_CODE_1013(1013, "账号不存在"),
+  ERROR_CODE_1014(1014, "当前账号不能删除"),
+  ERROR_CODE_1015(1015, "保存失败"),
+
+  ;
+  private int code;
+  private String msg;
+
+  ErrorCodeEnum(int code, String description) {
+    this.code = code;
+    this.msg = description;
+  }
+
+  public void setCode(int code) {
+    this.code = code;
+  }
+
+  public int getCode() {
+    return code;
+  }
+
+  public void setMsg(String description) {
+    this.msg = description;
+  }
+
+  public String getMsg() {
+    return msg;
+  }
+
+}

+ 36 - 0
mp-common/src/main/java/com/qs/mp/common/enums/HttpMethod.java

@@ -0,0 +1,36 @@
+package com.qs.mp.common.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.lang.Nullable;
+
+/**
+ * 请求方式
+ *
+ * @author ygp
+ */
+public enum HttpMethod
+{
+    GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
+
+    private static final Map<String, HttpMethod> mappings = new HashMap<>(16);
+
+    static
+    {
+        for (HttpMethod httpMethod : values())
+        {
+            mappings.put(httpMethod.name(), httpMethod);
+        }
+    }
+
+    @Nullable
+    public static HttpMethod resolve(@Nullable String method)
+    {
+        return (method != null ? mappings.get(method) : null);
+    }
+
+    public boolean matches(String method)
+    {
+        return (this == resolve(method));
+    }
+}

+ 20 - 0
mp-common/src/main/java/com/qs/mp/common/enums/LimitType.java

@@ -0,0 +1,20 @@
+package com.qs.mp.common.enums;
+
+/**
+ * 限流类型
+ *
+ * @author ygp
+ */
+
+public enum LimitType
+{
+    /**
+     * 默认策略全局限流
+     */
+    DEFAULT,
+
+    /**
+     * 根据请求者IP进行限流
+     */
+    IP
+}

+ 24 - 0
mp-common/src/main/java/com/qs/mp/common/enums/OperatorType.java

@@ -0,0 +1,24 @@
+package com.qs.mp.common.enums;
+
+/**
+ * 操作人类别
+ * 
+ * @author ygp
+ */
+public enum OperatorType
+{
+    /**
+     * 其它
+     */
+    OTHER,
+
+    /**
+     * 后台用户
+     */
+    MANAGE,
+
+    /**
+     * 手机端用户
+     */
+    MOBILE
+}

+ 27 - 0
mp-common/src/main/java/com/qs/mp/common/enums/PayOrderTypeEnum.java

@@ -0,0 +1,27 @@
+package com.qs.mp.common.enums;
+
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ *
+ * 支付订单枚举类型
+ *
+ */
+public enum PayOrderTypeEnum implements IEnum<Integer> {
+
+  ORDER_MERCH_RECHARGE(1, "商户充值");
+
+
+  private final int value;
+  private final String desc;
+
+  PayOrderTypeEnum(final int value, final String desc) {
+    this.value = value;
+    this.desc = desc;
+  }
+
+  @Override
+  public Integer getValue() {
+    return value;
+  }
+}

+ 54 - 0
mp-common/src/main/java/com/qs/mp/common/enums/RoleTypeEnum.java

@@ -0,0 +1,54 @@
+package com.qs.mp.common.enums;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.compress.utils.Lists;
+
+/**
+ * 用户类型
+ * @author zhongcp
+ * @Date 2021/9/1
+ */
+public enum RoleTypeEnum {
+	
+	  SYS_ROLE("1", "系统角色"),
+	  CUSTOMER_ROLE("2", "门店角色"),
+	  MERCHANT_ROLE("3", "商户端角色");
+	
+
+	  private String value;
+	  private String desc;
+
+	  public String getValue() {
+	    return value;
+	  }
+
+	  public void setValue(String value) {
+	    this.value = value;
+	  }
+
+	  public String getDesc() {
+	    return desc;
+	  }
+
+	  public void setDesc(String desc) {
+	    this.desc = desc;
+	  }
+
+	  private RoleTypeEnum(String value, String desc) {
+	    this.value = value;
+	    this.desc = desc;
+	  }
+	  
+	  public static List<Map<String, String>> toList() {
+        List<Map<String, String>> list = Lists.newArrayList();
+        for (RoleTypeEnum item : RoleTypeEnum.values()) {
+            Map<String, String> map = new HashMap<String, String>();
+            map.put("value", item.getValue());
+            map.put("desc", item.getDesc());
+            list.add(map);
+        }
+        return list;
+      }
+}

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