Browse Source

Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/1.8.0-uniapp

 Conflicts:
	yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
YunaiV 2 years atrás
parent
commit
b69b74b770
100 changed files with 1494 additions and 1586 deletions
  1. 39 40
      README.md
  2. 9 0
      http-client.env.json
  3. 31 27
      sql/mysql/ruoyi-vue-pro.sql
  4. 17 22
      sql/oracle/ruoyi-vue-pro.sql
  5. 5 101
      sql/postgresql/ruoyi-vue-pro.sql
  6. 11 3
      sql/sqlserver/ruoyi-vue-pro.sql
  7. 29 8
      yudao-dependencies/pom.xml
  8. 5 2
      yudao-framework/pom.xml
  9. 1 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java
  10. 60 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServerException.java
  11. 8 2
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java
  12. 5 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java
  13. 15 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
  14. 2 2
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java
  15. 25 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
  16. 30 0
      yudao-framework/yudao-spring-boot-starter-banner/pom.xml
  17. 20 0
      yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/config/YudaoBannerAutoConfiguration.java
  18. 4 4
      yudao-server/src/main/java/cn/iocoder/yudao/server/framework/tip/core/TipApplicationRunner.java
  19. 6 0
      yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/package-info.java
  20. 2 0
      yudao-framework/yudao-spring-boot-starter-banner/src/main/resources/META-INF/spring.factories
  21. 0 0
      yudao-framework/yudao-spring-boot-starter-banner/src/main/resources/banner.txt
  22. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDataPermissionAutoConfiguration.java
  23. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest.java
  24. 13 0
      yudao-framework/yudao-spring-boot-starter-biz-dict/pom.xml
  25. 3 3
      yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictAutoConfiguration.java
  26. 4 0
      yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/package-info.java
  27. 0 35
      yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/service/DictDataFrameworkService.java
  28. 51 9
      yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java
  29. 15 34
      yudao-framework/yudao-spring-boot-starter-extension/pom.xml
  30. 5 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/config/ErrorCodeProperties.java
  31. 15 13
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/config/ErrorCodeConfiguration.java
  32. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/generator/ErrorCodeAutoGenerator.java
  33. 6 6
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java
  34. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/loader/ErrorCodeLoader.java
  35. 6 6
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java
  36. 10 0
      yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/package-info.java
  37. 2 0
      yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/resources/META-INF/spring.factories
  38. 7 0
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml
  39. 8 0
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogAutoConfiguration.java
  40. 70 60
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java
  41. 0 87
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java
  42. 110 0
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java
  43. 8 8
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkService.java
  44. 28 0
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkServiceImpl.java
  45. 2 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
  46. 13 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java
  47. 203 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java
  48. 58 13
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXNativePayClient.java
  49. 0 3
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java
  50. 47 8
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java
  51. 0 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java
  52. 6 0
      yudao-framework/yudao-spring-boot-starter-biz-tenant/pom.xml
  53. 7 0
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
  54. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java
  55. 73 0
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkServiceImpl.java
  56. 0 2
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantContextWebFilter.java
  57. 7 8
      yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java
  58. 0 62
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/config/YudaoExtensionAutoConfiguration.java
  59. 0 142
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/BusinessScenario.java
  60. 0 41
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/ExtensionBootstrap.java
  61. 0 131
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/AbstractComponentExecutor.java
  62. 0 56
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/ExtensionContext.java
  63. 0 45
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/ExtensionContextHolder.java
  64. 0 28
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/ExtensionExecutor.java
  65. 0 96
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/factory/ExtensionDefinition.java
  66. 0 29
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/factory/ExtensionFactory.java
  67. 0 86
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/factory/ExtensionRegisterFactory.java
  68. 0 8
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/package-info.java
  69. 0 11
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/point/ExtensionPoint.java
  70. 0 41
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/stereotype/Extension.java
  71. 0 8
      yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/package-info.java
  72. 0 2
      yudao-framework/yudao-spring-boot-starter-extension/src/main/resources/META-INF/spring.factories
  73. 0 19
      yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/Application.java
  74. 0 43
      yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/ExtensionTest.java
  75. 0 8
      yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/package-info.java
  76. 0 22
      yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/PayExtensionPoint.java
  77. 0 40
      yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/command/TransactionsCommand.java
  78. 0 39
      yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/domain/TransactionsResult.java
  79. 0 26
      yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/impl/AlipayService.java
  80. 0 26
      yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/impl/WechatPayService.java
  81. 0 19
      yudao-framework/yudao-spring-boot-starter-extension/《芋道 Spring Boot 扩展点组件》.md
  82. 6 0
      yudao-framework/yudao-spring-boot-starter-monitor/pom.xml
  83. 27 0
      yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoMetricsAutoConfiguration.java
  84. 2 1
      yudao-framework/yudao-spring-boot-starter-monitor/src/main/resources/META-INF/spring.factories
  85. 3 3
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/SecurityProperties.java
  86. 2 2
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
  87. 2 2
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
  88. 7 0
      yudao-framework/yudao-spring-boot-starter-web/pom.xml
  89. 18 1
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogAutoConfiguration.java
  90. 4 4
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java
  91. 85 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java
  92. 2 6
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkService.java
  93. 28 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java
  94. 107 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java
  95. 2 6
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkService.java
  96. 28 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java
  97. 15 12
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
  98. 43 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/SpringFoxHandlerProviderBeanPostProcessor.java
  99. 6 2
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/WebProperties.java
  100. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java

+ 39 - 40
README.md

@@ -157,27 +157,27 @@ ps:核心功能已经实现,正在对接微信小程序中...
 
 | 框架                                                                                          | 说明               | 版本       | 学习指南                                                           |
 |---------------------------------------------------------------------------------------------|------------------|----------|----------------------------------------------------------------|
-| [Spring Boot](https://spring.io/projects/spring-boot)                                       | 应用开发框架           | 2.5.12   | [文档](https://github.com/YunaiV/SpringBoot-Labs)                |
+| [Spring Boot](https://spring.io/projects/spring-boot)                                       | 应用开发框架           | 2.6.8   | [文档](https://github.com/YunaiV/SpringBoot-Labs)                |
 | [MySQL](https://www.mysql.com/cn/)                                                          | 数据库服务器           | 5.7      |                                                                |
 | [Druid](https://github.com/alibaba/druid)                                                   | JDBC 连接池、监控组件    | 1.2.8    | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
-| [MyBatis Plus](https://mp.baomidou.com/)                                                    | MyBatis 增强工具包    | 3.5.1    | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao)         |
+| [MyBatis Plus](https://mp.baomidou.com/)                                                    | MyBatis 增强工具包    | 3.5.2    | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao)         |
 | [Dynamic Datasource](https://dynamic-datasource.com/)                                       | 动态数据源            | 3.5.0    | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
 | [Redis](https://redis.io/)                                                                  | key-value 数据库    | 5.0      |                                                                |
-| [Redisson](https://github.com/redisson/redisson)                                            | Redis 客户端        | 3.16.8   | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao)           |
-| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架           | 5.3.16   | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao)               |
-| [Spring Security](https://github.com/spring-projects/spring-security)                       | Spring 安全框架      | 5.5.5    | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
-| [Hibernate Validator](https://github.com/hibernate/hibernate-validator)                     | 参数校验组件           | 6.2.2    | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao)      |
+| [Redisson](https://github.com/redisson/redisson)                                            | Redis 客户端        | 3.17.3   | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao)           |
+| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架           | 5.3.20   | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao)               |
+| [Spring Security](https://github.com/spring-projects/spring-security)                       | Spring 安全框架      | 5.6.5    | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
+| [Hibernate Validator](https://github.com/hibernate/hibernate-validator)                     | 参数校验组件           | 6.2.3    | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao)      |
 | [Activiti](https://github.com/Activiti/Activiti)                                            | 工作流引擎            | 7.1.0.M6 | [文档](TODO)                                                     |
 | [Quartz](https://github.com/quartz-scheduler)                                               | 任务调度组件           | 2.3.2    | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao)             |
-| [Knife4j](https://gitee.com/xiaoym/knife4j)                                                 | Swagger 增强 UI 实现 | 3.0.2    | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao)         |
-| [Resilience4j](https://github.com/resilience4j/resilience4j)                                | 服务保障组件           | 1.7.0    | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao)    |
+| [Knife4j](https://gitee.com/xiaoym/knife4j)                                                 | Swagger 增强 UI 实现 | 3.0.3    | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao)         |
+| [Resilience4j](https://github.com/resilience4j/resilience4j)                                | 服务保障组件           | 1.7.1    | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao)    |
 | [SkyWalking](https://skywalking.apache.org/)                                                | 分布式应用追踪系统        | 8.5.0    | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao)      |
-| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin)                       | Spring Boot 监控平台 | 2.4.2    | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao)           |
-| [Jackson](https://github.com/FasterXML/jackson)                                             | JSON 工具库         | 2.12.6   |                                                                |
+| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin)                       | Spring Boot 监控平台 | 2.6.7    | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao)           |
+| [Jackson](https://github.com/FasterXML/jackson)                                             | JSON 工具库         | 2.13.3   |                                                                |
 | [MapStruct](https://mapstruct.org/)                                                         | Java Bean 转换     | 1.4.1    | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao)       |
 | [Lombok](https://projectlombok.org/)                                                        | 消除冗长的 Java 代码    | 1.16.14  | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao)          |
-| [JUnit](https://junit.org/junit5/)                                                          | Java 单元测试框架      | 5.7.2    | -                                                              |
-| [Mockito](https://github.com/mockito/mockito)                                               | Java Mock 框架     | 3.9.0    | -                                                              |
+| [JUnit](https://junit.org/junit5/)                                                          | Java 单元测试框架      | 5.8.2    | -                                                              |
+| [Mockito](https://github.com/mockito/mockito)                                               | Java Mock 框架     | 4.0.0    | -                                                              |
 
 ### 前端
 
@@ -192,42 +192,41 @@ ps:核心功能已经实现,正在对接微信小程序中...
 
 | 模块       | biu                                                                | biu                                                              | biu                                                              |
 |----------|--------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------|
-| 登录 & 首页  | ![登录](https://static.iocoder.cn/images/ruoyi-vue-pro/登录.jpg)       | ![首页](https://static.iocoder.cn/images/ruoyi-vue-pro/首页.jpg)     | ![个人中心](https://static.iocoder.cn/images/ruoyi-vue-pro/个人中心.jpg) |
-| 用户       | ![用户管理](https://static.iocoder.cn/images/ruoyi-vue-pro/用户管理.jpg)   | ![在线用户](https://static.iocoder.cn/images/ruoyi-vue-pro/在线用户.jpg) | -                                                                |
-| 租户 & 套餐  | ![租户管理](https://static.iocoder.cn/images/ruoyi-vue-pro/租户管理.jpg)   | ![租户套餐](https://static.iocoder.cn/images/ruoyi-vue-pro/租户套餐.png) | -                                                                |
-| 部门 & 岗位  | ![部门管理](https://static.iocoder.cn/images/ruoyi-vue-pro/部门管理.jpg)   | ![岗位管理](https://static.iocoder.cn/images/ruoyi-vue-pro/岗位管理.jpg) | -                                                                |
-| 菜单 & 角色  | ![菜单管理](https://static.iocoder.cn/images/ruoyi-vue-pro/菜单管理.jpg)   | ![角色管理](https://static.iocoder.cn/images/ruoyi-vue-pro/角色管理.jpg) | -                                                                |
-| 审计日志     | ![操作日志](https://static.iocoder.cn/images/ruoyi-vue-pro/操作日志.jpg)   | ![登录日志](https://static.iocoder.cn/images/ruoyi-vue-pro/登录日志.jpg) | -                                                                |
-| 短信       | ![短信渠道](https://static.iocoder.cn/images/ruoyi-vue-pro/短信渠道.jpg)   | ![短信模板](https://static.iocoder.cn/images/ruoyi-vue-pro/短信模板.jpg) | ![短信日志](https://static.iocoder.cn/images/ruoyi-vue-pro/短信日志.jpg) |
-| 字典       | ![字典类型](https://static.iocoder.cn/images/ruoyi-vue-pro/字典类型.jpg)   | ![字典数据](https://static.iocoder.cn/images/ruoyi-vue-pro/字典数据.jpg) | -                                                                |
-| 错误码 & 通知 | ![错误码管理](https://static.iocoder.cn/images/ruoyi-vue-pro/错误码管理.jpg) | ![通知公告](https://static.iocoder.cn/images/ruoyi-vue-pro/通知公告.jpg) | -                                                                |
+| 登录 & 首页  | ![登录](https://static.iocoder.cn/images/ruoyi-vue-pro/登录.jpg?imageView2/2/format/webp/w/1280)       | ![首页](https://static.iocoder.cn/images/ruoyi-vue-pro/首页.jpg?imageView2/2/format/webp/w/1280)     | ![个人中心](https://static.iocoder.cn/images/ruoyi-vue-pro/个人中心.jpg?imageView2/2/format/webp/w/1280) |
+| 用户       | ![用户管理](https://static.iocoder.cn/images/ruoyi-vue-pro/用户管理.jpg?imageView2/2/format/webp/w/1280)   | ![在线用户](https://static.iocoder.cn/images/ruoyi-vue-pro/在线用户.jpg?imageView2/2/format/webp/w/1280) | -                                                                |
+| 租户 & 套餐  | ![租户管理](https://static.iocoder.cn/images/ruoyi-vue-pro/租户管理.jpg?imageView2/2/format/webp/w/1280)   | ![租户套餐](https://static.iocoder.cn/images/ruoyi-vue-pro/租户套餐.png) | -                                                                |
+| 部门 & 岗位  | ![部门管理](https://static.iocoder.cn/images/ruoyi-vue-pro/部门管理.jpg?imageView2/2/format/webp/w/1280)   | ![岗位管理](https://static.iocoder.cn/images/ruoyi-vue-pro/岗位管理.jpg?imageView2/2/format/webp/w/1280) | -                                                                |
+| 菜单 & 角色  | ![菜单管理](https://static.iocoder.cn/images/ruoyi-vue-pro/菜单管理.jpg?imageView2/2/format/webp/w/1280)   | ![角色管理](https://static.iocoder.cn/images/ruoyi-vue-pro/角色管理.jpg?imageView2/2/format/webp/w/1280) | -                                                                |
+| 审计日志     | ![操作日志](https://static.iocoder.cn/images/ruoyi-vue-pro/操作日志.jpg?imageView2/2/format/webp/w/1280)   | ![登录日志](https://static.iocoder.cn/images/ruoyi-vue-pro/登录日志.jpg?imageView2/2/format/webp/w/1280) | -                                                                |
+| 短信       | ![短信渠道](https://static.iocoder.cn/images/ruoyi-vue-pro/短信渠道.jpg?imageView2/2/format/webp/w/1280)   | ![短信模板](https://static.iocoder.cn/images/ruoyi-vue-pro/短信模板.jpg?imageView2/2/format/webp/w/1280) | ![短信日志](https://static.iocoder.cn/images/ruoyi-vue-pro/短信日志.jpg?imageView2/2/format/webp/w/1280) |
+| 字典       | ![字典类型](https://static.iocoder.cn/images/ruoyi-vue-pro/字典类型.jpg?imageView2/2/format/webp/w/1280)   | ![字典数据](https://static.iocoder.cn/images/ruoyi-vue-pro/字典数据.jpg?imageView2/2/format/webp/w/1280) | -                                                                |
+| 错误码 & 通知 | ![错误码管理](https://static.iocoder.cn/images/ruoyi-vue-pro/错误码管理.jpg?imageView2/2/format/webp/w/1280) | ![通知公告](https://static.iocoder.cn/images/ruoyi-vue-pro/通知公告.jpg?imageView2/2/format/webp/w/1280) | -                                                                |
 
 ### 工作流程
 
 | 模块      | biu                                                                    | biu                                                                    | biu                                                                    |
 |---------|------------------------------------------------------------------------|------------------------------------------------------------------------|------------------------------------------------------------------------|
-| 流程模型    | ![流程模型-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-列表.jpg) | ![流程模型-设计](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-设计.jpg) | ![流程模型-定义](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-定义.jpg) |
-| 表单 & 分组 | ![流程表单](https://static.iocoder.cn/images/ruoyi-vue-pro/流程表单.jpg)       | ![用户分组](https://static.iocoder.cn/images/ruoyi-vue-pro/用户分组.jpg)       | -                                                                      |
-| 我的流程    | ![我的流程-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-列表.jpg) | ![我的流程-发起](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-发起.jpg) | ![我的流程-详情](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-详情.jpg) |
-| 待办 & 已办 | ![任务列表-审批](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-审批.jpg) | ![任务列表-待办](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-待办.jpg) | ![任务列表-已办](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-已办.jpg) |
-| OA 请假   | ![OA请假-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/OA请假-列表.jpg) | ![OA请假-发起](https://static.iocoder.cn/images/ruoyi-vue-pro/OA请假-发起.jpg) | ![OA请假-详情](https://static.iocoder.cn/images/ruoyi-vue-pro/OA请假-详情.jpg) |
-
-### 支付系统
-
-| 模块      | biu                                                              | biu                                                                    | biu                                                                    |
-|---------|------------------------------------------------------------------|------------------------------------------------------------------------|------------------------------------------------------------------------|
-| 商家 & 应用 | ![商户信息](https://static.iocoder.cn/images/ruoyi-vue-pro/商户信息.jpg) | ![应用信息-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/应用信息-列表.jpg) | ![应用信息-编辑](https://static.iocoder.cn/images/ruoyi-vue-pro/应用信息-编辑.jpg) |
-| 支付 & 退款 | ![支付订单](https://static.iocoder.cn/images/ruoyi-vue-pro/支付订单.jpg) | ![退款订单](https://static.iocoder.cn/images/ruoyi-vue-pro/退款订单.jpg)       | ---                                                                    |
+| 流程模型    | ![流程模型-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-列表.jpg?imageView2/2/format/webp/w/1280) | ![流程模型-设计](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-设计.jpg?imageView2/2/format/webp/w/1280) | ![流程模型-定义](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-定义.jpg?imageView2/2/format/webp/w/1280) |
+| 表单 & 分组 | ![流程表单](https://static.iocoder.cn/images/ruoyi-vue-pro/流程表单.jpg?imageView2/2/format/webp/w/1280)       | ![用户分组](https://static.iocoder.cn/images/ruoyi-vue-pro/用户分组.jpg?imageView2/2/format/webp/w/1280)       | -                                                                      |
+| 我的流程    | ![我的流程-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-列表.jpg?imageView2/2/format/webp/w/1280) | ![我的流程-发起](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-发起.jpg?imageView2/2/format/webp/w/1280) | ![我的流程-详情](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-详情.jpg?imageView2/2/format/webp/w/1280) |
+| 待办 & 已办 | ![任务列表-审批](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-审批.jpg?imageView2/2/format/webp/w/1280) | ![任务列表-待办](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-待办.jpg?imageView2/2/format/webp/w/1280) | ![任务列表-已办](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-已办.jpg?imageView2/2/format/webp/w/1280) |
+| OA 请假   | ![OA请假-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/OA请假-列表.jpg?imageView2/2/format/webp/w/1280) | ![OA请假-发起](https://static.iocoder.cn/images/ruoyi-vue-pro/OA请假-发起.jpg?imageView2/2/format/webp/w/1280) | ![OA请假-详情](https://static.iocoder.cn/images/ruoyi-vue-pro/OA请假-详情.jpg?imageView2/2/format/webp/w/1280) |
 
 ### 基础设施
 
 | 模块            | biu                                                                  | biu                                                                | biu                                                              |
 |---------------|----------------------------------------------------------------------|--------------------------------------------------------------------|------------------------------------------------------------------|
-| 代码生成          | ![代码生成](https://static.iocoder.cn/images/ruoyi-vue-pro/代码生成.jpg)     | ![生成效果](https://static.iocoder.cn/images/ruoyi-vue-pro/生成效果.jpg)   | -                                                                |
-| 文档            | ![系统接口](https://static.iocoder.cn/images/ruoyi-vue-pro/系统接口.jpg)     | ![数据库文档](https://static.iocoder.cn/images/ruoyi-vue-pro/数据库文档.jpg) | -                                                                |
-| 文件 & 配置       | ![文件配置](https://static.iocoder.cn/images/ruoyi-vue-pro/文件配置.jpg) | ![文件管理](https://static.iocoder.cn/images/ruoyi-vue-pro/文件管理2.jpg)     | ![配置管理](https://static.iocoder.cn/images/ruoyi-vue-pro/配置管理.jpg)   |
-| 定时任务          | ![定时任务](https://static.iocoder.cn/images/ruoyi-vue-pro/定时任务.jpg)     | ![任务日志](https://static.iocoder.cn/images/ruoyi-vue-pro/任务日志.jpg)   | -                                                                |
-| API 日志        | ![访问日志](https://static.iocoder.cn/images/ruoyi-vue-pro/访问日志.jpg)     | ![错误日志](https://static.iocoder.cn/images/ruoyi-vue-pro/错误日志.jpg)   | -                                                                |
-| MySQL & Redis | ![MySQL](https://static.iocoder.cn/images/ruoyi-vue-pro/MySQL.jpg)   | ![Redis](https://static.iocoder.cn/images/ruoyi-vue-pro/Redis.jpg) | -                                                                |
-| 监控平台          | ![Java监控](https://static.iocoder.cn/images/ruoyi-vue-pro/Java监控.jpg) | ![链路追踪](https://static.iocoder.cn/images/ruoyi-vue-pro/链路追踪.jpg)   | ![日志中心](https://static.iocoder.cn/images/ruoyi-vue-pro/日志中心.jpg) |
+| 代码生成          | ![代码生成](https://static.iocoder.cn/images/ruoyi-vue-pro/代码生成.jpg?imageView2/2/format/webp/w/1280)     | ![生成效果](https://static.iocoder.cn/images/ruoyi-vue-pro/生成效果.jpg?imageView2/2/format/webp/w/1280)   | -                                                                |
+| 文档            | ![系统接口](https://static.iocoder.cn/images/ruoyi-vue-pro/系统接口.jpg?imageView2/2/format/webp/w/1280)     | ![数据库文档](https://static.iocoder.cn/images/ruoyi-vue-pro/数据库文档.jpg?imageView2/2/format/webp/w/1280) | -                                                                |
+| 文件 & 配置       | ![文件配置](https://static.iocoder.cn/images/ruoyi-vue-pro/文件配置.jpg?imageView2/2/format/webp/w/1280) | ![文件管理](https://static.iocoder.cn/images/ruoyi-vue-pro/文件管理2.jpg?imageView2/2/format/webp/w/1280)     | ![配置管理](https://static.iocoder.cn/images/ruoyi-vue-pro/配置管理.jpg?imageView2/2/format/webp/w/1280)   |
+| 定时任务          | ![定时任务](https://static.iocoder.cn/images/ruoyi-vue-pro/定时任务.jpg?imageView2/2/format/webp/w/1280)     | ![任务日志](https://static.iocoder.cn/images/ruoyi-vue-pro/任务日志.jpg?imageView2/2/format/webp/w/1280)   | -                                                                |
+| API 日志        | ![访问日志](https://static.iocoder.cn/images/ruoyi-vue-pro/访问日志.jpg?imageView2/2/format/webp/w/1280)     | ![错误日志](https://static.iocoder.cn/images/ruoyi-vue-pro/错误日志.jpg?imageView2/2/format/webp/w/1280)   | -                                                                |
+| MySQL & Redis | ![MySQL](https://static.iocoder.cn/images/ruoyi-vue-pro/MySQL.jpg?imageView2/2/format/webp/w/1280)   | ![Redis](https://static.iocoder.cn/images/ruoyi-vue-pro/Redis.jpg?imageView2/2/format/webp/w/1280) | -                                                                |
+| 监控平台          | ![Java监控](https://static.iocoder.cn/images/ruoyi-vue-pro/Java监控.jpg?imageView2/2/format/webp/w/1280) | ![链路追踪](https://static.iocoder.cn/images/ruoyi-vue-pro/链路追踪.jpg?imageView2/2/format/webp/w/1280)   | ![日志中心](https://static.iocoder.cn/images/ruoyi-vue-pro/日志中心.jpg?imageView2/2/format/webp/w/1280) |
 
+### 支付系统
+
+| 模块      | biu                                                              | biu                                                                    | biu                                                                    |
+|---------|------------------------------------------------------------------|------------------------------------------------------------------------|------------------------------------------------------------------------|
+| 商家 & 应用 | ![商户信息](https://static.iocoder.cn/images/ruoyi-vue-pro/商户信息.jpg?imageView2/2/format/webp/w/1280) | ![应用信息-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/应用信息-列表.jpg?imageView2/2/format/webp/w/1280) | ![应用信息-编辑](https://static.iocoder.cn/images/ruoyi-vue-pro/应用信息-编辑.jpg?imageView2/2/format/webp/w/1280) |
+| 支付 & 退款 | ![支付订单](https://static.iocoder.cn/images/ruoyi-vue-pro/支付订单.jpg?imageView2/2/format/webp/w/1280) | ![退款订单](https://static.iocoder.cn/images/ruoyi-vue-pro/退款订单.jpg?imageView2/2/format/webp/w/1280)       | ---                                                                    |

+ 9 - 0
http-client.env.json

@@ -7,5 +7,14 @@
     "appApi": "http://127.0.0.1:48080/app-api",
     "appToken": "test1",
     "appTenentId": "1"
+  },
+  "gateway": {
+    "baseUrl": "http://127.0.0.1:8888/admin-api",
+    "token": "test1",
+    "adminTenentId": "1",
+
+    "appApi": "http://127.0.0.1:8888/app-api",
+    "appToken": "test1",
+    "appTenentId": "1"
   }
 }

File diff suppressed because it is too large
+ 31 - 27
sql/mysql/ruoyi-vue-pro.sql


+ 17 - 22
sql/oracle/ruoyi-vue-pro.sql

@@ -11,7 +11,7 @@
  Target Server Version : 110200
  File Encoding         : 65001
 
- Date: 26/05/2022 00:38:28
+ Date: 15/06/2022 08:20:08
 */
 
 
@@ -884,7 +884,8 @@ CREATE TABLE "INFRA_FILE" (
   "CREATE_TIME" DATE NOT NULL,
   "UPDATER" NVARCHAR2(64),
   "UPDATE_TIME" DATE NOT NULL,
-  "DELETED" NUMBER(1,0) DEFAULT 0
+  "DELETED" NUMBER(1,0) DEFAULT 0,
+  "NAME" NVARCHAR2(512)
 )
 LOGGING
 NOCOMPRESS
@@ -913,6 +914,7 @@ COMMENT ON COLUMN "INFRA_FILE"."CREATOR" IS '创建者';
 COMMENT ON COLUMN "INFRA_FILE"."CREATE_TIME" IS '创建时间';
 COMMENT ON COLUMN "INFRA_FILE"."UPDATER" IS '更新者';
 COMMENT ON COLUMN "INFRA_FILE"."UPDATE_TIME" IS '更新时间';
+COMMENT ON COLUMN "INFRA_FILE"."NAME" IS '文件名';
 COMMENT ON TABLE "INFRA_FILE" IS '文件表';
 
 -- ----------------------------
@@ -3196,7 +3198,7 @@ COMMENT ON COLUMN "SYSTEM_OAUTH2_CLIENT"."STATUS" IS '状态';
 COMMENT ON COLUMN "SYSTEM_OAUTH2_CLIENT"."ACCESS_TOKEN_VALIDITY_SECONDS" IS '访问令牌的有效期';
 COMMENT ON COLUMN "SYSTEM_OAUTH2_CLIENT"."REFRESH_TOKEN_VALIDITY_SECONDS" IS '刷新令牌的有效期';
 COMMENT ON COLUMN "SYSTEM_OAUTH2_CLIENT"."REDIRECT_URIS" IS '可重定向的 URI 地址';
-COMMENT ON COLUMN "SYSTEM_OAUTH2_CLIENT"."AUTO_APPROVE_SCOPES" IS '是否自动授权';
+COMMENT ON COLUMN "SYSTEM_OAUTH2_CLIENT"."AUTO_APPROVE_SCOPES" IS '自动通过的授权范围';
 COMMENT ON COLUMN "SYSTEM_OAUTH2_CLIENT"."AUTHORIZED_GRANT_TYPES" IS '授权类型';
 COMMENT ON COLUMN "SYSTEM_OAUTH2_CLIENT"."SCOPES" IS '授权范围';
 COMMENT ON COLUMN "SYSTEM_OAUTH2_CLIENT"."AUTHORITIES" IS '权限';
@@ -3795,7 +3797,7 @@ COMMIT;
 DROP TABLE "SYSTEM_SMS_CHANNEL";
 CREATE TABLE "SYSTEM_SMS_CHANNEL" (
   "ID" NUMBER(20,0) NOT NULL,
-  "SIGNATURE" NVARCHAR2(10),
+  "SIGNATURE" NVARCHAR2(12),
   "CODE" NVARCHAR2(63),
   "STATUS" NUMBER(4,0) NOT NULL,
   "REMARK" NVARCHAR2(255),
@@ -5393,10 +5395,8 @@ ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008671" CHECK ("STATE" IS
 -- Indexes structure for table QRTZ_FIRED_TRIGGERS
 -- ----------------------------
 CREATE INDEX "IDX_QRTZ_FT_INST_JOB_REQ_RCVRY"
-  ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "INSTANCE_NAME" ASC, "REQUESTS_RECOVERY" ASC) LOCAL
+  ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "INSTANCE_NAME" ASC, "REQUESTS_RECOVERY" ASC)
   LOGGING
-  ONLINE
-  NOSORT
   VISIBLE
 PCTFREE 10
 INITRANS 2
@@ -5410,7 +5410,7 @@ STORAGE (
   BUFFER_POOL DEFAULT
 );
 CREATE INDEX "IDX_QRTZ_FT_JG"
-  ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "JOB_GROUP" ASC) LOCAL
+  ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "JOB_GROUP" ASC)
   LOGGING
   ONLINE
   NOSORT
@@ -5427,10 +5427,8 @@ STORAGE (
   BUFFER_POOL DEFAULT
 );
 CREATE INDEX "IDX_QRTZ_FT_J_G"
-  ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "JOB_NAME" ASC, "JOB_GROUP" ASC) LOCAL
+  ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "JOB_NAME" ASC, "JOB_GROUP" ASC)
   LOGGING
-  ONLINE
-  NOSORT
   VISIBLE
 PCTFREE 10
 INITRANS 2
@@ -5446,7 +5444,6 @@ STORAGE (
 CREATE INDEX "IDX_QRTZ_FT_TG"
   ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "TRIGGER_GROUP" ASC) LOCAL
   LOGGING
-  ONLINE
   NOSORT
   VISIBLE
 PCTFREE 10
@@ -5499,8 +5496,6 @@ STORAGE (
 CREATE INDEX "IDX_QRTZ_J_REQ_RECOVERY"
   ON "QRTZ_JOB_DETAILS" ("SCHED_NAME" ASC, "REQUESTS_RECOVERY" ASC) LOCAL
   LOGGING
-  ONLINE
-  NOSORT
   VISIBLE
 PCTFREE 10
 INITRANS 2
@@ -5622,8 +5617,10 @@ ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008696" CHECK ("START_TIME" IS
 -- Indexes structure for table QRTZ_TRIGGERS
 -- ----------------------------
 CREATE INDEX "IDX_QRTZ_T_C"
-  ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "CALENDAR_NAME" ASC)
+  ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "CALENDAR_NAME" ASC) LOCAL
   LOGGING
+  ONLINE
+  NOSORT
   VISIBLE
 PCTFREE 10
 INITRANS 2
@@ -5637,10 +5634,8 @@ STORAGE (
   BUFFER_POOL DEFAULT
 );
 CREATE INDEX "IDX_QRTZ_T_J"
-  ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "JOB_NAME" ASC, "JOB_GROUP" ASC) LOCAL
+  ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "JOB_NAME" ASC, "JOB_GROUP" ASC)
   LOGGING
-  ONLINE
-  NOSORT
   VISIBLE
 PCTFREE 10
 INITRANS 2
@@ -5654,8 +5649,10 @@ STORAGE (
   BUFFER_POOL DEFAULT
 );
 CREATE INDEX "IDX_QRTZ_T_JG"
-  ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "JOB_GROUP" ASC)
+  ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "JOB_GROUP" ASC) LOCAL
   LOGGING
+  ONLINE
+  NOSORT
   VISIBLE
 PCTFREE 10
 INITRANS 2
@@ -5669,10 +5666,8 @@ STORAGE (
   BUFFER_POOL DEFAULT
 );
 CREATE INDEX "IDX_QRTZ_T_NEXT_FIRE_TIME"
-  ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "NEXT_FIRE_TIME" ASC) LOCAL
+  ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "NEXT_FIRE_TIME" ASC)
   LOGGING
-  ONLINE
-  NOSORT
   VISIBLE
 PCTFREE 10
 INITRANS 2

+ 5 - 101
sql/postgresql/ruoyi-vue-pro.sql

@@ -12,7 +12,7 @@
  Target Server Version : 140002
  File Encoding         : 65001
 
- Date: 26/05/2022 00:10:12
+ Date: 15/06/2022 08:13:44
 */
 
 
@@ -365,17 +365,6 @@ START 1
 CACHE 1;
 
 -- ----------------------------
-DROP SEQUENCE IF EXISTS "system_oauth2_approve_seq";
-CREATE SEQUENCE "system_oauth2_approve_seq" 
-INCREMENT 1
-MINVALUE  1
-MAXVALUE 9223372036854775807
-START 1
-CACHE 1;
-
 -- Sequence structure for system_oauth2_client_seq
 -- ----------------------------
 DROP SEQUENCE IF EXISTS "system_oauth2_client_seq";
@@ -387,17 +376,6 @@ START 1
 CACHE 1;
 
 -- ----------------------------
-DROP SEQUENCE IF EXISTS "system_oauth2_code_seq";
-CREATE SEQUENCE "system_oauth2_code_seq" 
-INCREMENT 1
-MINVALUE  1
-MAXVALUE 9223372036854775807
-START 1
-CACHE 1;
-
 -- Sequence structure for system_oauth2_refresh_token_seq
 -- ----------------------------
 DROP SEQUENCE IF EXISTS "system_oauth2_refresh_token_seq";
@@ -1745,7 +1723,8 @@ CREATE TABLE "infra_file" (
   "create_time" timestamp(6) NOT NULL,
   "updater" varchar(64) COLLATE "pg_catalog"."default",
   "update_time" timestamp(6) NOT NULL,
-  "deleted" int2 NOT NULL DEFAULT 0
+  "deleted" int2 NOT NULL DEFAULT 0,
+  "name" varchar(255) COLLATE "pg_catalog"."default"
 )
 ;
 COMMENT ON COLUMN "infra_file"."id" IS '文件编号';
@@ -1759,6 +1738,7 @@ COMMENT ON COLUMN "infra_file"."create_time" IS '创建时间';
 COMMENT ON COLUMN "infra_file"."updater" IS '更新者';
 COMMENT ON COLUMN "infra_file"."update_time" IS '更新时间';
 COMMENT ON COLUMN "infra_file"."deleted" IS '是否删除';
+COMMENT ON COLUMN "infra_file"."name" IS '文件名';
 COMMENT ON TABLE "infra_file" IS '文件表';
 
 -- ----------------------------
@@ -3413,47 +3393,6 @@ BEGIN;
 COMMIT;
 
 -- ----------------------------
-DROP TABLE IF EXISTS "system_oauth2_approve";
-CREATE TABLE "system_oauth2_approve" (
-  "id" int8 NOT NULL,
-  "user_id" int8 NOT NULL,
-  "user_type" int2 NOT NULL,
-  "client_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
-  "scope" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
-  "approved" bool NOT NULL,
-  "expires_time" timestamp(6) NOT NULL,
-  "creator" varchar(64) COLLATE "pg_catalog"."default",
-  "create_time" timestamp(6) NOT NULL,
-  "updater" varchar(64) COLLATE "pg_catalog"."default",
-  "update_time" timestamp(6) NOT NULL,
-  "deleted" int2 NOT NULL DEFAULT 0,
-  "tenant_id" int8 NOT NULL
-)
-;
-COMMENT ON COLUMN "system_oauth2_approve"."id" IS '编号';
-COMMENT ON COLUMN "system_oauth2_approve"."user_id" IS '用户编号';
-COMMENT ON COLUMN "system_oauth2_approve"."user_type" IS '用户类型';
-COMMENT ON COLUMN "system_oauth2_approve"."client_id" IS '客户端编号';
-COMMENT ON COLUMN "system_oauth2_approve"."scope" IS '授权范围';
-COMMENT ON COLUMN "system_oauth2_approve"."approved" IS '是否接受';
-COMMENT ON COLUMN "system_oauth2_approve"."expires_time" IS '过期时间';
-COMMENT ON COLUMN "system_oauth2_approve"."creator" IS '创建者';
-COMMENT ON COLUMN "system_oauth2_approve"."create_time" IS '创建时间';
-COMMENT ON COLUMN "system_oauth2_approve"."updater" IS '更新者';
-COMMENT ON COLUMN "system_oauth2_approve"."update_time" IS '更新时间';
-COMMENT ON COLUMN "system_oauth2_approve"."deleted" IS '是否删除';
-COMMENT ON COLUMN "system_oauth2_approve"."tenant_id" IS '租户编号';
-COMMENT ON TABLE "system_oauth2_approve" IS 'OAuth2 批准表';
-
-BEGIN;
-COMMIT;
-
 -- Table structure for system_oauth2_client
 -- ----------------------------
 DROP TABLE IF EXISTS "system_oauth2_client";
@@ -3513,51 +3452,6 @@ INSERT INTO "system_oauth2_client" ("id", "client_id", "secret", "name", "logo",
 COMMIT;
 
 -- ----------------------------
-DROP TABLE IF EXISTS "system_oauth2_code";
-CREATE TABLE "system_oauth2_code" (
-  "id" int8 NOT NULL,
-  "user_id" int8 NOT NULL,
-  "user_type" int2 NOT NULL,
-  "code" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
-  "client_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
-  "scopes" varchar(255) COLLATE "pg_catalog"."default",
-  "expires_time" timestamp(6) NOT NULL,
-  "redirect_uri" varchar(255) COLLATE "pg_catalog"."default",
-  "state" varchar(255) COLLATE "pg_catalog"."default",
-  "creator" varchar(64) COLLATE "pg_catalog"."default",
-  "create_time" timestamp(6) NOT NULL,
-  "updater" varchar(64) COLLATE "pg_catalog"."default",
-  "update_time" timestamp(6) NOT NULL,
-  "deleted" int2 NOT NULL DEFAULT 0,
-  "tenant_id" int8 NOT NULL
-)
-;
-COMMENT ON COLUMN "system_oauth2_code"."id" IS '编号';
-COMMENT ON COLUMN "system_oauth2_code"."user_id" IS '用户编号';
-COMMENT ON COLUMN "system_oauth2_code"."user_type" IS '用户类型';
-COMMENT ON COLUMN "system_oauth2_code"."code" IS '授权码';
-COMMENT ON COLUMN "system_oauth2_code"."client_id" IS '客户端编号';
-COMMENT ON COLUMN "system_oauth2_code"."scopes" IS '授权范围';
-COMMENT ON COLUMN "system_oauth2_code"."expires_time" IS '过期时间';
-COMMENT ON COLUMN "system_oauth2_code"."redirect_uri" IS '可重定向的 URI 地址';
-COMMENT ON COLUMN "system_oauth2_code"."state" IS '状态';
-COMMENT ON COLUMN "system_oauth2_code"."creator" IS '创建者';
-COMMENT ON COLUMN "system_oauth2_code"."create_time" IS '创建时间';
-COMMENT ON COLUMN "system_oauth2_code"."updater" IS '更新者';
-COMMENT ON COLUMN "system_oauth2_code"."update_time" IS '更新时间';
-COMMENT ON COLUMN "system_oauth2_code"."deleted" IS '是否删除';
-COMMENT ON COLUMN "system_oauth2_code"."tenant_id" IS '租户编号';
-COMMENT ON TABLE "system_oauth2_code" IS 'OAuth2 授权码表';
-
-BEGIN;
-COMMIT;
-
 -- Table structure for system_oauth2_refresh_token
 -- ----------------------------
 DROP TABLE IF EXISTS "system_oauth2_refresh_token";
@@ -4025,7 +3919,7 @@ COMMIT;
 DROP TABLE IF EXISTS "system_sms_channel";
 CREATE TABLE "system_sms_channel" (
   "id" int8 NOT NULL,
-  "signature" varchar(10) COLLATE "pg_catalog"."default" NOT NULL,
+  "signature" varchar(12) COLLATE "pg_catalog"."default" NOT NULL,
   "code" varchar(63) COLLATE "pg_catalog"."default" NOT NULL,
   "status" int2 NOT NULL,
   "remark" varchar(255) COLLATE "pg_catalog"."default",
@@ -4729,21 +4623,11 @@ SELECT setval('"system_oauth2_access_token_seq"', 11, true);
 -- ----------------------------
 -- Alter sequences owned by
 -- ----------------------------
-SELECT setval('"system_oauth2_approve_seq"', 4, true);
-
 SELECT setval('"system_oauth2_client_seq"', 1, false);
 
 -- ----------------------------
 -- Alter sequences owned by
 -- ----------------------------
-SELECT setval('"system_oauth2_code_seq"', 4, true);
-
 SELECT setval('"system_oauth2_refresh_token_seq"', 1, true);
 
 -- ----------------------------
@@ -5169,21 +5053,11 @@ ALTER TABLE "system_notice" ADD CONSTRAINT "system_notice_pkey" PRIMARY KEY ("id
 ALTER TABLE "system_oauth2_access_token" ADD CONSTRAINT "system_oauth2_access_token_pkey" PRIMARY KEY ("id");
 
 -- ----------------------------
-ALTER TABLE "system_oauth2_approve" ADD CONSTRAINT "system_oauth2_approve_pkey" PRIMARY KEY ("id");
-
 -- Primary Key structure for table system_oauth2_client
 -- ----------------------------
 ALTER TABLE "system_oauth2_client" ADD CONSTRAINT "system_oauth2_client_pkey" PRIMARY KEY ("id");
 
 -- ----------------------------
-ALTER TABLE "system_oauth2_code" ADD CONSTRAINT "system_oauth2_code_pkey" PRIMARY KEY ("id");
-
 -- Primary Key structure for table system_oauth2_refresh_token
 -- ----------------------------
 ALTER TABLE "system_oauth2_refresh_token" ADD CONSTRAINT "system_oauth2_refresh_token_pkey" PRIMARY KEY ("id");

+ 11 - 3
sql/sqlserver/ruoyi-vue-pro.sql

@@ -12,7 +12,7 @@
  Target Server Version : 15004198
  File Encoding         : 65001
 
- Date: 26/05/2022 01:01:02
+ Date: 15/06/2022 08:15:45
 */
 
 
@@ -2640,7 +2640,8 @@ CREATE TABLE [dbo].[infra_file] (
   [create_time] datetime2(7)  NOT NULL,
   [updater] nvarchar(64) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,
   [update_time] datetime2(7)  NOT NULL,
-  [deleted] bit DEFAULT 0 NOT NULL
+  [deleted] bit DEFAULT 0 NOT NULL,
+  [name] nvarchar(256) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL
 )
 GO
 
@@ -2725,6 +2726,13 @@ EXEC sp_addextendedproperty
 GO
 
 EXEC sp_addextendedproperty
+'MS_Description', N'文件路径',
+'SCHEMA', N'dbo',
+'TABLE', N'infra_file',
+'COLUMN', N'name'
+GO
+
+EXEC sp_addextendedproperty
 'MS_Description', N'文件表',
 'SCHEMA', N'dbo',
 'TABLE', N'infra_file'
@@ -9490,7 +9498,7 @@ GO
 
 CREATE TABLE [dbo].[system_sms_channel] (
   [id] bigint  IDENTITY(1,1) NOT NULL,
-  [signature] nvarchar(10) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,
+  [signature] nvarchar(12) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,
   [code] nvarchar(63) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,
   [status] tinyint  NOT NULL,
   [remark] nvarchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS  NULL,

+ 29 - 8
yudao-dependencies/pom.xml

@@ -16,31 +16,31 @@
     <properties>
         <revision>1.6.2-snapshot</revision>
         <!-- 统一依赖管理 -->
-        <spring.boot.version>2.5.12</spring.boot.version>
+        <spring.boot.version>2.6.8</spring.boot.version>
         <!-- Web 相关 -->
-        <knife4j.version>3.0.2</knife4j.version>
-        <swagger-annotations.version>1.5.22</swagger-annotations.version>
+        <knife4j.version>3.0.3</knife4j.version>
+        <swagger-annotations.version>1.6.6</swagger-annotations.version>
         <servlet.versoin>2.5</servlet.versoin>
         <!-- DB 相关 -->
         <druid.version>1.2.8</druid.version>
-        <mybatis-plus.version>3.4.3.4</mybatis-plus.version>
+        <mybatis-plus.version>3.5.2</mybatis-plus.version>
         <mybatis-plus-generator.version>3.5.2</mybatis-plus-generator.version>
         <dynamic-datasource.version>3.5.0</dynamic-datasource.version>
-        <redisson.version>3.17.0</redisson.version>
+        <redisson.version>3.17.3</redisson.version>
         <!-- Config 配置中心相关 -->
         <apollo.version>1.9.2</apollo.version>
         <!-- Job 定时任务相关 -->
         <!-- 服务保障相关 -->
         <lock4j.version>2.2.0</lock4j.version>
-        <resilience4j.version>1.7.0</resilience4j.version>
+        <resilience4j.version>1.7.1</resilience4j.version>
         <!-- 监控相关 -->
         <skywalking.version>8.7.0</skywalking.version>
-        <spring-boot-admin.version>2.6.2</spring-boot-admin.version>
+        <spring-boot-admin.version>2.6.7</spring-boot-admin.version>
         <opentracing.version>0.31.0</opentracing.version>
         <!-- Test 测试相关 -->
         <podam.version>7.2.6.RELEASE</podam.version>
         <jedis-mock.version>0.1.16</jedis-mock.version>
-        <mockito-inline.version>3.9.0</mockito-inline.version>
+        <mockito-inline.version>4.0.0</mockito-inline.version>
         <!-- Bpm 工作流相关 -->
         <activiti.version>7.1.0.M6</activiti.version>
         <flowable.version>6.7.0</flowable.version>
@@ -52,6 +52,7 @@
         <easyexcel.verion>2.2.7</easyexcel.verion>
         <velocity.version>2.2</velocity.version>
         <screw.version>1.0.5</screw.version>
+		<fastjson.version>2.0.5</fastjson.version>
         <guava.version>30.1.1-jre</guava.version>
         <guice.version>5.1.0</guice.version>
         <transmittable-thread-local.version>2.12.2</transmittable-thread-local.version>
@@ -80,6 +81,11 @@
             <!-- 业务组件 -->
             <dependency>
                 <groupId>cn.iocoder.boot</groupId>
+                <artifactId>yudao-spring-boot-starter-banner</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>cn.iocoder.boot</groupId>
                 <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
                 <version>${revision}</version>
             </dependency>
@@ -118,6 +124,11 @@
                 <artifactId>yudao-spring-boot-starter-biz-social</artifactId>
                 <version>${revision}</version>
             </dependency>
+            <dependency>
+                <groupId>cn.iocoder.boot</groupId>
+                <artifactId>yudao-spring-boot-starter-biz-error-code</artifactId>
+                <version>${revision}</version>
+            </dependency>
 
             <!-- Spring 核心 -->
             <dependency>
@@ -476,6 +487,12 @@
             </dependency>
 
             <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>fastjson</artifactId>
+                <version>${fastjson.version}</version>
+            </dependency>
+
+            <dependency>
                 <groupId>cn.smallbun.screw</groupId>
                 <artifactId>screw-core</artifactId> <!-- 实现数据库文档 -->
                 <version>${screw.version}</version>
@@ -484,6 +501,10 @@
                         <groupId>org.freemarker</groupId>
                         <artifactId>freemarker</artifactId> <!-- 移除 Freemarker 依赖,采用 Velocity 作为模板引擎 -->
                     </exclusion>
+				    <exclusion>
+                        <groupId>com.alibaba</groupId>
+                        <artifactId>fastjson</artifactId> <!-- 最新版screw-core1.0.5依赖fastjson1.2.73存在漏洞,移除。 -->
+                    </exclusion>
                 </exclusions>
             </dependency>
 

+ 5 - 2
yudao-framework/pom.xml

@@ -11,6 +11,7 @@
     <packaging>pom</packaging>
     <modules>
         <module>yudao-common</module>
+        <module>yudao-spring-boot-starter-banner</module>
         <module>yudao-spring-boot-starter-mybatis</module>
         <module>yudao-spring-boot-starter-redis</module>
         <module>yudao-spring-boot-starter-web</module>
@@ -25,17 +26,19 @@
 
         <module>yudao-spring-boot-starter-excel</module>
         <module>yudao-spring-boot-starter-test</module>
-        <module>yudao-spring-boot-starter-extension</module>
 
         <module>yudao-spring-boot-starter-biz-operatelog</module>
         <module>yudao-spring-boot-starter-biz-dict</module>
         <module>yudao-spring-boot-starter-biz-sms</module>
-        <module>yudao-spring-boot-starter-activiti</module>
+
         <module>yudao-spring-boot-starter-biz-pay</module>
         <module>yudao-spring-boot-starter-biz-weixin</module>
         <module>yudao-spring-boot-starter-biz-social</module>
         <module>yudao-spring-boot-starter-biz-tenant</module>
         <module>yudao-spring-boot-starter-biz-data-permission</module>
+        <module>yudao-spring-boot-starter-biz-error-code</module>
+
+        <module>yudao-spring-boot-starter-activiti</module>
         <module>yudao-spring-boot-starter-flowable</module>
     </modules>
 

+ 1 - 1
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.common.enums;
 /**
  * Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
  *
- *  考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下
+ *  考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enums 包下
  *
  * @author 芋道源码
  */

+ 60 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServerException.java

@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.framework.common.exception;
+
+import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 服务器异常 Exception
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public final class ServerException extends RuntimeException {
+
+    /**
+     * 全局错误码
+     *
+     * @see GlobalErrorCodeConstants
+     */
+    private Integer code;
+    /**
+     * 错误提示
+     */
+    private String message;
+
+    /**
+     * 空构造方法,避免反序列化问题
+     */
+    public ServerException() {
+    }
+
+    public ServerException(ErrorCode errorCode) {
+        this.code = errorCode.getCode();
+        this.message = errorCode.getMsg();
+    }
+
+    public ServerException(Integer code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public ServerException setCode(Integer code) {
+        this.code = code;
+        return this;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    public ServerException setMessage(String message) {
+        this.message = message;
+        return this;
+    }
+
+}

+ 8 - 2
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java

@@ -36,9 +36,15 @@ public interface GlobalErrorCodeConstants {
 
     ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
 
-   static boolean isMatch(Integer code) {
+    /**
+     * 是否为服务端错误,参考 HTTP 5XX 错误码段
+     *
+     * @param code 错误码
+     * @return 是否
+     */
+   static boolean isServerErrorCode(Integer code) {
        return code != null
-               && code >= SUCCESS.getCode() && code <= UNKNOWN.getCode();
+               && code >= INTERNAL_SERVER_ERROR.getCode() && code <= INTERNAL_SERVER_ERROR.getCode() + 99;
    }
 
 }

+ 5 - 1
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java

@@ -29,6 +29,10 @@ package cn.iocoder.yudao.framework.common.exception.enums;
  */
 public class ServiceErrorCodeRange {
 
-    // 模块 system 错误码区间 [1-000-001-000 ~ 1-000-002-000]
+    // 模块 infra 错误码区间 [1-001-000-000 ~ 1-002-000-000)
+    // 模块 system 错误码区间 [1-002-000-000 ~ 1-003-000-000)
+    // 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)
+    // 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)
+    // 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)
 
 }

+ 15 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.framework.common.pojo;
 
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+import cn.iocoder.yudao.framework.common.exception.ServerException;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -91,10 +92,24 @@ public class CommonResult<T> implements Serializable {
         if (isSuccess()) {
             return;
         }
+        // 服务端异常
+        if (GlobalErrorCodeConstants.isServerErrorCode(code)) {
+            throw new ServerException(code, msg);
+        }
         // 业务异常
         throw new ServiceException(code, msg);
     }
 
+    /**
+     * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常
+     * 如果没有,则返回 {@link #data} 数据
+     */
+    @JsonIgnore // 避免 jackson 序列化
+    public T getCheckedData() {
+        checkError();
+        return data;
+    }
+
     public static <T> CommonResult<T> error(ServiceException serviceException) {
         return error(serviceException.getCode(), serviceException.getMessage());
     }

+ 2 - 2
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java

@@ -23,8 +23,8 @@ public class PageParam implements Serializable {
 
     @ApiModelProperty(value = "每页条数,最大值为 100", required = true, example = "10")
     @NotNull(message = "每页条数不能为空")
-    @Min(value = 1, message = "页码最小值为 1")
-    @Max(value = 100, message = "页码最大值为 100")
+    @Min(value = 1, message = "每页条数最小值为 1")
+    @Max(value = 100, message = "每页条数最大值为 100")
     private Integer pageSize = PAGE_SIZE;
 
 }

+ 25 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.common.util.cache;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+import java.time.Duration;
+import java.util.concurrent.Executors;
+
+/**
+ * Cache 工具类
+ *
+ * @author 芋道源码
+ */
+public class CacheUtils {
+
+    public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {
+        return CacheBuilder.newBuilder()
+                // 只阻塞当前数据加载线程,其他线程返回旧值
+                .refreshAfterWrite(duration)
+                // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
+                .build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置
+    }
+
+}

+ 30 - 0
yudao-framework/yudao-spring-boot-starter-banner/pom.xml

@@ -0,0 +1,30 @@
+<?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>yudao-framework</artifactId>
+        <groupId>cn.iocoder.boot</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-spring-boot-starter-banner</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>Banner 用于在 console 控制台,打印开发文档、接口文档等</description>
+    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 20 - 0
yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/config/YudaoBannerAutoConfiguration.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.framework.banner.config;
+
+import cn.iocoder.yudao.framework.banner.core.BannerApplicationRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Banner 的自动配置类
+ *
+ * @author 芋道源码
+ */
+@Configuration
+public class YudaoBannerAutoConfiguration {
+
+    @Bean
+    public BannerApplicationRunner bannerApplicationRunner() {
+        return new BannerApplicationRunner();
+    }
+
+}

+ 4 - 4
yudao-server/src/main/java/cn/iocoder/yudao/server/framework/tip/core/TipApplicationRunner.java

@@ -1,19 +1,19 @@
-package cn.iocoder.yudao.server.framework.tip.core;
+package cn.iocoder.yudao.framework.banner.core;
 
 import cn.hutool.core.thread.ThreadUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.ApplicationArguments;
 import org.springframework.boot.ApplicationRunner;
-import org.springframework.stereotype.Component;
 
 import java.util.concurrent.TimeUnit;
 
 /**
  * 项目启动成功后,提供文档相关的地址
+ *
+ * @author 芋道源码
  */
-@Component
 @Slf4j
-public class TipApplicationRunner implements ApplicationRunner {
+public class BannerApplicationRunner implements ApplicationRunner {
 
     @Override
     public void run(ApplicationArguments args) throws Exception {

+ 6 - 0
yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * Banner 用于在 console 控制台,打印开发文档、接口文档等
+ *
+ * @author 芋道源码
+ */
+package cn.iocoder.yudao.framework.banner;

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-banner/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  cn.iocoder.yudao.framework.banner.config.YudaoBannerAutoConfiguration

yudao-server/src/main/resources/banner.txt → yudao-framework/yudao-spring-boot-starter-banner/src/main/resources/banner.txt


+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDataPermissionAutoConfiguration.java

@@ -13,7 +13,7 @@ import org.springframework.context.annotation.Configuration;
 import java.util.List;
 
 /**
- * 数据全新啊的自动配置类
+ * 数据权限的自动配置类
  *
  * @author 芋道源码
  */

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest.java

@@ -62,7 +62,7 @@ public class DataPermissionDatabaseInterceptorTest extends BaseMockitoUnitTest {
             // 调用
             interceptor.beforeQuery(null, mappedStatement, null, null, null, boundSql);
             // 断言
-            pluginUtilsMock.verify(never(), () -> PluginUtils.mpBoundSql(boundSql));
+            pluginUtilsMock.verify(() -> PluginUtils.mpBoundSql(boundSql), never());
         }
     }
 

+ 13 - 0
yudao-framework/yudao-spring-boot-starter-biz-dict/pom.xml

@@ -26,5 +26,18 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter</artifactId>
         </dependency>
+
+        <!-- 业务组件 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行 Token 的校验 -->
+            <version>${revision}</version>
+        </dependency>
+
+        <!-- 工具类相关 -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 3 - 3
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictAutoConfiguration.java

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.framework.dict.config;
 
-import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService;
 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
+import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -10,8 +10,8 @@ public class YudaoDictAutoConfiguration {
 
     @Bean
     @SuppressWarnings("InstantiationOfUtilityClass")
-    public DictFrameworkUtils dictUtils(DictDataFrameworkService service) {
-        DictFrameworkUtils.init(service);
+    public DictFrameworkUtils dictUtils(DictDataApi dictDataApi) {
+        DictFrameworkUtils.init(dictDataApi);
         return new DictFrameworkUtils();
     }
 

+ 4 - 0
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 占位
+ */
+package cn.iocoder.yudao.framework.dict.core;

+ 0 - 35
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/service/DictDataFrameworkService.java

@@ -1,35 +0,0 @@
-package cn.iocoder.yudao.framework.dict.core.service;
-
-import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
-
-import java.util.List;
-
-public interface DictDataFrameworkService {
-
-    /**
-     * 获得指定的字典数据,从缓存中
-     *
-     * @param type 字典类型
-     * @param value 字典数据值
-     * @return 字典数据
-     */
-    DictDataRespDTO getDictDataFromCache(String type, String value);
-
-    /**
-     * 解析获得指定的字典数据,从缓存中
-     *
-     * @param type 字典类型
-     * @param label 字典数据标签
-     * @return 字典数据
-     */
-    DictDataRespDTO parseDictDataFromCache(String type, String label);
-
-    /**
-     * 获得指定类型的字典数据,从缓存中
-     *
-     * @param type 字典类型
-     * @return 字典数据列表
-     */
-    List<DictDataRespDTO> listDictDatasFromCache(String type);
-
-}

+ 51 - 9
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java

@@ -1,28 +1,70 @@
 package cn.iocoder.yudao.framework.dict.core.util;
 
-import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
-import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService;
+import cn.hutool.core.util.ObjectUtil;
+import cn.iocoder.yudao.framework.common.core.KeyValue;
+import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
+import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
+import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 
+import java.time.Duration;
+
 /**
  * 字典工具类
+ *
+ * @author 芋道源码
  */
 @Slf4j
 public class DictFrameworkUtils {
 
-    private static DictDataFrameworkService service;
+    private static DictDataApi dictDataApi;
+
+    private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO();
+
+    /**
+     * 针对 {@link #getDictDataLabel(String, String)} 的缓存
+     */
+    private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> getDictDataCache = CacheUtils.buildAsyncReloadingCache(
+            Duration.ofMinutes(1L), // 过期时间 1 分钟
+            new CacheLoader<KeyValue<String, String>, DictDataRespDTO>() {
+
+                @Override
+                public DictDataRespDTO load(KeyValue<String, String> key) {
+                    return ObjectUtil.defaultIfNull(dictDataApi.getDictData(key.getKey(), key.getValue()), DICT_DATA_NULL);
+                }
+
+            });
+
+    /**
+     * 针对 {@link #parseDictDataValue(String, String)} 的缓存
+     */
+    private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> parseDictDataCache = CacheUtils.buildAsyncReloadingCache(
+            Duration.ofMinutes(1L), // 过期时间 1 分钟
+            new CacheLoader<KeyValue<String, String>, DictDataRespDTO>() {
+
+                @Override
+                public DictDataRespDTO load(KeyValue<String, String> key) {
+                    return ObjectUtil.defaultIfNull(dictDataApi.parseDictData(key.getKey(), key.getValue()), DICT_DATA_NULL);
+                }
+
+            });
 
-    public static void init(DictDataFrameworkService service) {
-        DictFrameworkUtils.service = service;
+    public static void init(DictDataApi dictDataApi) {
+        DictFrameworkUtils.dictDataApi = dictDataApi;
         log.info("[init][初始化 DictFrameworkUtils 成功]");
     }
 
-    public static DictDataRespDTO getDictDataFromCache(String type, String value) {
-        return service.getDictDataFromCache(type, value);
+    @SneakyThrows
+    public static String getDictDataLabel(String dictType, String value) {
+        return getDictDataCache.get(new KeyValue<>(dictType, value)).getLabel();
     }
 
-    public static DictDataRespDTO parseDictDataFromCache(String type, String label) {
-        return service.parseDictDataFromCache(type, label);
+    @SneakyThrows
+    public static String parseDictDataValue(String dictType, String label) {
+        return parseDictDataCache.get(new KeyValue<>(dictType, label)).getValue();
     }
 
 }

+ 15 - 34
yudao-framework/yudao-spring-boot-starter-extension/pom.xml

@@ -8,18 +8,18 @@
         <version>${revision}</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>yudao-spring-boot-starter-extension</artifactId>
+    <artifactId>yudao-spring-boot-starter-biz-error-code</artifactId>
     <packaging>jar</packaging>
 
     <name>${project.artifactId}</name>
-    <description>扩展点组件</description>
+    <description>
+        错误码 ErrorCode 的自动配置功能,提供如下功能:
+        1. 远程读取:项目启动时,从 system-server 服务,读取数据库中的 ErrorCode 错误码,实现错误码的提水可配置;
+        2. 自动更新:管理员在管理后台修数据库中的 ErrorCode 错误码时,项目自动从 system-server 服务加载最新的 ErrorCode 错误码;
+        3. 自动写入:项目启动时,将项目本地的错误码写到 system-server 服务中,方便管理员在管理后台编辑;
+    </description>
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
-    <properties>
-
-    </properties>
-
     <dependencies>
         <dependency>
             <groupId>cn.iocoder.boot</groupId>
@@ -28,41 +28,22 @@
 
         <!-- Spring 核心 -->
         <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-context</artifactId>
-            <scope>provided</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-beans</artifactId>
-            <scope>provided</scope>
-        </dependency>
-
-        <!-- Spring 核心 -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-aop</artifactId>
-        </dependency>
-
-        <!-- 测试包 -->
-        <dependency>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-test</artifactId>
-            <scope>test</scope>
+            <artifactId>spring-boot-starter</artifactId>
         </dependency>
 
-        <!-- 测试包 -->
+        <!-- 业务组件 -->
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
+            <version>${revision}</version>
         </dependency>
 
-        <!-- 工具类相关 -->
         <dependency>
             <groupId>jakarta.validation</groupId>
             <artifactId>jakarta.validation-api</artifactId>
+            <scope>provided</scope> <!-- 设置为 provided,主要是 ErrorCodeProperties 使用到 -->
         </dependency>
     </dependencies>
-</project>
+
+</project>

+ 5 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/config/ErrorCodeProperties.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.system.framework.errorcode.config;
+package cn.iocoder.yudao.framework.errorcode.config;
 
 import lombok.Data;
 import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -18,6 +18,10 @@ import java.util.List;
 public class ErrorCodeProperties {
 
     /**
+     * 是否开启
+     */
+    private Boolean enable = true;
+    /**
      * 错误码枚举类
      */
     @NotNull(message = "错误码枚举类不能为空")

+ 15 - 13
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/config/ErrorCodeConfiguration.java

@@ -1,37 +1,39 @@
-package cn.iocoder.yudao.module.system.framework.errorcode.config;
+package cn.iocoder.yudao.framework.errorcode.config;
 
-import cn.iocoder.yudao.module.system.framework.errorcode.core.generator.ErrorCodeAutoGenerator;
-import cn.iocoder.yudao.module.system.framework.errorcode.core.loader.ErrorCodeLoader;
-import cn.iocoder.yudao.module.system.framework.errorcode.core.service.ErrorCodeFrameworkService;
-import cn.iocoder.yudao.module.system.framework.errorcode.core.loader.ErrorCodeLoaderImpl;
-import cn.iocoder.yudao.module.system.framework.errorcode.core.generator.ErrorCodeAutoGeneratorImpl;
+import cn.iocoder.yudao.framework.errorcode.core.generator.ErrorCodeAutoGenerator;
+import cn.iocoder.yudao.framework.errorcode.core.generator.ErrorCodeAutoGeneratorImpl;
+import cn.iocoder.yudao.framework.errorcode.core.loader.ErrorCodeLoader;
+import cn.iocoder.yudao.framework.errorcode.core.loader.ErrorCodeLoaderImpl;
+import cn.iocoder.yudao.module.system.api.errorcode.ErrorCodeApi;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.annotation.EnableScheduling;
 
-// TODO 芋艿:貌似放的位置有问题
 /**
  * 错误码配置类
+ *
+ * @author 芋道源码
  */
 @Configuration
+@ConditionalOnProperty(prefix = "yudao.error-code", value = "enable", matchIfMissing = true) // 允许使用 yudao.error-code.enable=false 禁用访问日志
 @EnableConfigurationProperties(ErrorCodeProperties.class)
 @EnableScheduling // 开启调度任务的功能,因为 ErrorCodeRemoteLoader 通过定时刷新错误码
-public class ErrorCodeConfiguration {
+public class YudaoErrorCodeConfiguration {
 
     @Bean
     public ErrorCodeAutoGenerator errorCodeAutoGenerator(@Value("${spring.application.name}") String applicationName,
                                                          ErrorCodeProperties errorCodeProperties,
-                                                         ErrorCodeFrameworkService errorCodeFrameworkService) {
-        return new ErrorCodeAutoGeneratorImpl(applicationName, errorCodeProperties.getConstantsClassList(),
-                errorCodeFrameworkService);
+                                                         ErrorCodeApi errorCodeApi) {
+        return new ErrorCodeAutoGeneratorImpl(applicationName, errorCodeProperties.getConstantsClassList(), errorCodeApi);
     }
 
     @Bean
     public ErrorCodeLoader errorCodeLoader(@Value("${spring.application.name}") String applicationName,
-                                           ErrorCodeFrameworkService errorCodeFrameworkService) {
-        return new ErrorCodeLoaderImpl(applicationName, errorCodeFrameworkService);
+                                           ErrorCodeApi errorCodeApi) {
+        return new ErrorCodeLoaderImpl(applicationName, errorCodeApi);
     }
 
 }

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/generator/ErrorCodeAutoGenerator.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.system.framework.errorcode.core.generator;
+package cn.iocoder.yudao.framework.errorcode.core.generator;
 
 /**
  * 错误码的自动生成器

+ 6 - 6
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java

@@ -1,12 +1,12 @@
-package cn.iocoder.yudao.module.system.framework.errorcode.core.generator;
+package cn.iocoder.yudao.framework.errorcode.core.generator;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.util.ClassUtil;
 import cn.hutool.core.util.ReflectUtil;
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
-import cn.iocoder.yudao.module.system.framework.errorcode.core.dto.ErrorCodeAutoGenerateReqDTO;
-import cn.iocoder.yudao.module.system.framework.errorcode.core.service.ErrorCodeFrameworkService;
+import cn.iocoder.yudao.module.system.api.errorcode.ErrorCodeApi;
+import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeAutoGenerateReqDTO;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.context.event.ApplicationReadyEvent;
@@ -36,9 +36,9 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {
      */
     private final List<String> constantsClassList;
     /**
-     * 错误码 Service
+     * 错误码 Api
      */
-    private final ErrorCodeFrameworkService errorCodeService;
+    private final ErrorCodeApi errorCodeApi;
 
     @Override
     @EventListener(ApplicationReadyEvent.class)
@@ -49,7 +49,7 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {
         log.info("[execute][解析到错误码数量为 ({}) 个]", autoGenerateDTOs.size());
 
         // 第二步,写入到 system 服务
-        errorCodeService.autoGenerateErrorCodes(autoGenerateDTOs);
+        errorCodeApi.autoGenerateErrorCodes(autoGenerateDTOs);
         log.info("[execute][写入到 system 组件完成]");
     }
 

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/loader/ErrorCodeLoader.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.system.framework.errorcode.core.loader;
+package cn.iocoder.yudao.framework.errorcode.core.loader;
 
 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 

+ 6 - 6
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java

@@ -1,9 +1,9 @@
-package cn.iocoder.yudao.module.system.framework.errorcode.core.loader;
+package cn.iocoder.yudao.framework.errorcode.core.loader;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.iocoder.yudao.module.system.framework.errorcode.core.dto.ErrorCodeRespDTO;
-import cn.iocoder.yudao.module.system.framework.errorcode.core.service.ErrorCodeFrameworkService;
 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
+import cn.iocoder.yudao.module.system.api.errorcode.ErrorCodeApi;
+import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeRespDTO;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.context.event.ApplicationReadyEvent;
@@ -34,9 +34,9 @@ public class ErrorCodeLoaderImpl implements ErrorCodeLoader {
      */
     private final String applicationName;
     /**
-     * 错误码 Service
+     * 错误码 Api
      */
-    private final ErrorCodeFrameworkService errorCodeService;
+    private final ErrorCodeApi errorCodeApi;
 
     /**
      * 缓存错误码的最大更新时间,用于后续的增量轮询,判断是否有更新
@@ -55,7 +55,7 @@ public class ErrorCodeLoaderImpl implements ErrorCodeLoader {
 
     private void loadErrorCodes0() {
         // 加载错误码
-        List<ErrorCodeRespDTO> errorCodeRespDTOs = errorCodeService.getErrorCodeList(applicationName, maxUpdateTime);
+        List<ErrorCodeRespDTO> errorCodeRespDTOs = errorCodeApi.getErrorCodeList(applicationName, maxUpdateTime);
         if (CollUtil.isEmpty(errorCodeRespDTOs)) {
             return;
         }

+ 10 - 0
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/package-info.java

@@ -0,0 +1,10 @@
+/**
+ * 错误码 ErrorCode 的自动配置功能,提供如下功能:
+ *
+ * 1. 远程读取:项目启动时,从 system-service 服务,读取数据库中的 ErrorCode 错误码,实现错误码的提水可配置;
+ * 2. 自动更新:管理员在管理后台修数据库中的 ErrorCode 错误码时,项目自动从 system-service 服务加载最新的 ErrorCode 错误码;
+ * 3. 自动写入:项目启动时,将项目本地的错误码写到 system-server 服务中,方便管理员在管理后台编辑;
+ *
+ * @author 芋道源码
+ */
+package cn.iocoder.yudao.framework.errorcode;

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  cn.iocoder.yudao.framework.errorcode.config.YudaoErrorCodeConfiguration

+ 7 - 0
yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml

@@ -34,6 +34,13 @@
             <scope>provided</scope>
         </dependency>
 
+        <!-- 业务组件 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
+            <version>${revision}</version>
+        </dependency>
+
         <!-- 工具类相关 -->
         <dependency>
             <groupId>com.google.guava</groupId>

+ 8 - 0
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogAutoConfiguration.java

@@ -1,6 +1,9 @@
 package cn.iocoder.yudao.framework.operatelog.config;
 
 import cn.iocoder.yudao.framework.operatelog.core.aop.OperateLogAspect;
+import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkService;
+import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkServiceImpl;
+import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -12,4 +15,9 @@ public class YudaoOperateLogAutoConfiguration {
         return new OperateLogAspect();
     }
 
+    @Bean
+    public OperateLogFrameworkService operateLogFrameworkService(OperateLogApi operateLogApi) {
+        return new OperateLogFrameworkServiceImpl(operateLogApi);
+    }
+
 }

+ 70 - 60
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java

@@ -9,9 +9,8 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
-import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
-import cn.iocoder.yudao.framework.operatelog.core.dto.OperateLogCreateReqDTO;
 import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
+import cn.iocoder.yudao.framework.operatelog.core.service.OperateLog;
 import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkService;
 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
 import com.google.common.collect.Maps;
@@ -45,7 +44,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC
  * 满足如下任一条件,则会进行记录:
  * 1. 使用 @ApiOperation + 非 @GetMapping
  * 2. 使用 @OperateLog 注解
- *
+ * <p>
  * 但是,如果声明 @OperateLog 注解时,将 enable 属性设置为 false 时,强制不记录。
  *
  * @author 芋道源码
@@ -57,13 +56,13 @@ public class OperateLogAspect {
     /**
      * 用于记录操作内容的上下文
      *
-     * @see OperateLogCreateReqDTO#getContent()
+     * @see OperateLog#getContent()
      */
     private static final ThreadLocal<String> CONTENT = new ThreadLocal<>();
     /**
      * 用于记录拓展字段的上下文
      *
-     * @see OperateLogCreateReqDTO#getExts()
+     * @see OperateLog#getExts()
      */
     private static final ThreadLocal<Map<String, Object>> EXTS = new ThreadLocal<>();
 
@@ -73,16 +72,21 @@ public class OperateLogAspect {
     @Around("@annotation(apiOperation)")
     public Object around(ProceedingJoinPoint joinPoint, ApiOperation apiOperation) throws Throwable {
         // 可能也添加了 @ApiOperation 注解
-        OperateLog operateLog = getMethodAnnotation(joinPoint, OperateLog.class);
+        cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog = getMethodAnnotation(joinPoint,
+                cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog.class);
         return around0(joinPoint, operateLog, apiOperation);
     }
 
-    @Around("!@annotation(io.swagger.annotations.ApiOperation) && @annotation(operateLog)") // 兼容处理,只添加 @OperateLog 注解的情况
-    public Object around(ProceedingJoinPoint joinPoint, OperateLog operateLog) throws Throwable {
+    @Around("!@annotation(io.swagger.annotations.ApiOperation) && @annotation(operateLog)")
+    // 兼容处理,只添加 @OperateLog 注解的情况
+    public Object around(ProceedingJoinPoint joinPoint,
+                         cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog) throws Throwable {
         return around0(joinPoint, operateLog, null);
     }
 
-    private Object around0(ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation) throws Throwable {
+    private Object around0(ProceedingJoinPoint joinPoint,
+                           cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
+                           ApiOperation apiOperation) throws Throwable {
         // 目前,只有管理员,才记录操作日志!所以非管理员,直接调用,不进行记录
         Integer userType = WebFrameworkUtils.getLoginUserType();
         if (!Objects.equals(userType, UserTypeEnum.ADMIN.getValue())) {
@@ -121,7 +125,9 @@ public class OperateLogAspect {
         EXTS.remove();
     }
 
-    private void log(ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation,
+    private void log(ProceedingJoinPoint joinPoint,
+                     cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
+                     ApiOperation apiOperation,
                      Date startTime, Object result, Throwable exception) {
         try {
             // 判断不记录的情况
@@ -136,113 +142,117 @@ public class OperateLogAspect {
         }
     }
 
-    private void log0(ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation,
+    private void log0(ProceedingJoinPoint joinPoint,
+                      cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
+                      ApiOperation apiOperation,
                       Date startTime, Object result, Throwable exception) {
-        OperateLogCreateReqDTO operateLogDTO = new OperateLogCreateReqDTO();
+        OperateLog operateLogObj = new OperateLog();
         // 补全通用字段
-        operateLogDTO.setTraceId(TracerUtils.getTraceId());
-        operateLogDTO.setStartTime(startTime);
+        operateLogObj.setTraceId(TracerUtils.getTraceId());
+        operateLogObj.setStartTime(startTime);
         // 补充用户信息
-        fillUserFields(operateLogDTO);
+        fillUserFields(operateLogObj);
         // 补全模块信息
-        fillModuleFields(operateLogDTO, joinPoint, operateLog, apiOperation);
+        fillModuleFields(operateLogObj, joinPoint, operateLog, apiOperation);
         // 补全请求信息
-        fillRequestFields(operateLogDTO);
+        fillRequestFields(operateLogObj);
         // 补全方法信息
-        fillMethodFields(operateLogDTO, joinPoint, operateLog, startTime, result, exception);
+        fillMethodFields(operateLogObj, joinPoint, operateLog, startTime, result, exception);
 
         // 异步记录日志
-        operateLogFrameworkService.createOperateLogAsync(operateLogDTO);
+        operateLogFrameworkService.createOperateLog(operateLogObj);
     }
 
-    private static void fillUserFields(OperateLogCreateReqDTO operateLogDTO) {
-        operateLogDTO.setUserId(WebFrameworkUtils.getLoginUserId());
-        operateLogDTO.setUserType(WebFrameworkUtils.getLoginUserType());
+    private static void fillUserFields(OperateLog operateLogObj) {
+        operateLogObj.setUserId(WebFrameworkUtils.getLoginUserId());
+        operateLogObj.setUserType(WebFrameworkUtils.getLoginUserType());
     }
 
-    private static void fillModuleFields(OperateLogCreateReqDTO operateLogDTO,
-                                         ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation) {
+    private static void fillModuleFields(OperateLog operateLogObj,
+                                         ProceedingJoinPoint joinPoint,
+                                         cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
+                                         ApiOperation apiOperation) {
         // module 属性
         if (operateLog != null) {
-            operateLogDTO.setModule(operateLog.module());
+            operateLogObj.setModule(operateLog.module());
         }
-        if (StrUtil.isEmpty(operateLogDTO.getModule())) {
+        if (StrUtil.isEmpty(operateLogObj.getModule())) {
             Api api = getClassAnnotation(joinPoint, Api.class);
             if (api != null) {
                 // 优先读取 @API 的 name 属性
                 if (StrUtil.isNotEmpty(api.value())) {
-                    operateLogDTO.setModule(api.value());
+                    operateLogObj.setModule(api.value());
                 }
                 // 没有的话,读取 @API 的 tags 属性
-                if (StrUtil.isEmpty(operateLogDTO.getModule()) && ArrayUtil.isNotEmpty(api.tags())) {
-                    operateLogDTO.setModule(api.tags()[0]);
+                if (StrUtil.isEmpty(operateLogObj.getModule()) && ArrayUtil.isNotEmpty(api.tags())) {
+                    operateLogObj.setModule(api.tags()[0]);
                 }
             }
         }
         // name 属性
         if (operateLog != null) {
-            operateLogDTO.setName(operateLog.name());
+            operateLogObj.setName(operateLog.name());
         }
-        if (StrUtil.isEmpty(operateLogDTO.getName()) && apiOperation != null) {
-            operateLogDTO.setName(apiOperation.value());
+        if (StrUtil.isEmpty(operateLogObj.getName()) && apiOperation != null) {
+            operateLogObj.setName(apiOperation.value());
         }
         // type 属性
         if (operateLog != null && ArrayUtil.isNotEmpty(operateLog.type())) {
-            operateLogDTO.setType(operateLog.type()[0].getType());
+            operateLogObj.setType(operateLog.type()[0].getType());
         }
-        if (operateLogDTO.getType() == null) {
+        if (operateLogObj.getType() == null) {
             RequestMethod requestMethod = obtainFirstMatchRequestMethod(obtainRequestMethod(joinPoint));
             OperateTypeEnum operateLogType = convertOperateLogType(requestMethod);
-            operateLogDTO.setType(operateLogType != null ? operateLogType.getType() : null);
+            operateLogObj.setType(operateLogType != null ? operateLogType.getType() : null);
         }
         // content 和 exts 属性
-        operateLogDTO.setContent(CONTENT.get());
-        operateLogDTO.setExts(EXTS.get());
+        operateLogObj.setContent(CONTENT.get());
+        operateLogObj.setExts(EXTS.get());
     }
 
-    private static void fillRequestFields(OperateLogCreateReqDTO operateLogDTO) {
+    private static void fillRequestFields(OperateLog operateLogObj) {
         // 获得 Request 对象
         HttpServletRequest request = ServletUtils.getRequest();
         if (request == null) {
             return;
         }
         // 补全请求信息
-        operateLogDTO.setRequestMethod(request.getMethod());
-        operateLogDTO.setRequestUrl(request.getRequestURI());
-        operateLogDTO.setUserIp(ServletUtil.getClientIP(request));
-        operateLogDTO.setUserAgent(ServletUtils.getUserAgent(request));
+        operateLogObj.setRequestMethod(request.getMethod());
+        operateLogObj.setRequestUrl(request.getRequestURI());
+        operateLogObj.setUserIp(ServletUtil.getClientIP(request));
+        operateLogObj.setUserAgent(ServletUtils.getUserAgent(request));
     }
 
-    private static void fillMethodFields(OperateLogCreateReqDTO operateLogDTO,
-                                         ProceedingJoinPoint joinPoint, OperateLog operateLog,
+    private static void fillMethodFields(OperateLog operateLogObj,
+                                         ProceedingJoinPoint joinPoint,
+                                         cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
                                          Date startTime, Object result, Throwable exception) {
         MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
-        operateLogDTO.setJavaMethod(methodSignature.toString());
+        operateLogObj.setJavaMethod(methodSignature.toString());
         if (operateLog == null || operateLog.logArgs()) {
-            operateLogDTO.setJavaMethodArgs(obtainMethodArgs(joinPoint));
+            operateLogObj.setJavaMethodArgs(obtainMethodArgs(joinPoint));
         }
         if (operateLog == null || operateLog.logResultData()) {
-            operateLogDTO.setResultData(obtainResultData(result));
+            operateLogObj.setResultData(obtainResultData(result));
         }
-        operateLogDTO.setDuration((int) (System.currentTimeMillis() - startTime.getTime()));
+        operateLogObj.setDuration((int) (System.currentTimeMillis() - startTime.getTime()));
         // (正常)处理 resultCode 和 resultMsg 字段
-        if (result != null) {
-            if (result instanceof CommonResult) {
-                CommonResult<?> commonResult = (CommonResult<?>) result;
-                operateLogDTO.setResultCode(commonResult.getCode());
-                operateLogDTO.setResultMsg(commonResult.getMsg());
-            } else {
-                operateLogDTO.setResultCode(SUCCESS.getCode());
-            }
+        if (result instanceof CommonResult) {
+            CommonResult<?> commonResult = (CommonResult<?>) result;
+            operateLogObj.setResultCode(commonResult.getCode());
+            operateLogObj.setResultMsg(commonResult.getMsg());
+        } else {
+            operateLogObj.setResultCode(SUCCESS.getCode());
         }
         // (异常)处理 resultCode 和 resultMsg 字段
         if (exception != null) {
-            operateLogDTO.setResultCode(INTERNAL_SERVER_ERROR.getCode());
-            operateLogDTO.setResultMsg(ExceptionUtil.getRootCauseMessage(exception));
+            operateLogObj.setResultCode(INTERNAL_SERVER_ERROR.getCode());
+            operateLogObj.setResultMsg(ExceptionUtil.getRootCauseMessage(exception));
         }
     }
 
-    private static boolean isLogEnable(ProceedingJoinPoint joinPoint, OperateLog operateLog) {
+    private static boolean isLogEnable(ProceedingJoinPoint joinPoint,
+                                       cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog) {
         // 有 @OperateLog 注解的情况下
         if (operateLog != null) {
             return operateLog.enable();
@@ -256,7 +266,7 @@ public class OperateLogAspect {
             return null;
         }
         return Arrays.stream(requestMethods).filter(requestMethod ->
-                           requestMethod == RequestMethod.POST
+                requestMethod == RequestMethod.POST
                         || requestMethod == RequestMethod.PUT
                         || requestMethod == RequestMethod.DELETE)
                 .findFirst().orElse(null);

+ 0 - 87
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java

@@ -1,87 +0,0 @@
-package cn.iocoder.yudao.framework.operatelog.core.dto;
-
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotEmpty;
-import javax.validation.constraints.NotNull;
-import java.util.Date;
-import java.util.Map;
-
-/**
- * 操作日志创建 Request DTO
- */
-@Data
-public class OperateLogCreateReqDTO {
-
-    @ApiModelProperty(value = "链路追踪编号", required = true, example = "89aca178-a370-411c-ae02-3f0d672be4ab")
-    @NotEmpty(message = "链路追踪编号不能为空")
-    private String traceId;
-
-    @ApiModelProperty(value = "用户编号", required = true, example = "1024")
-    @NotNull(message = "用户编号不能为空")
-    private Long userId;
-    @ApiModelProperty(value = "用户类型", required = true, example = "1")
-    @NotNull(message = "用户类型不能为空")
-    private Integer userType;
-
-    @ApiModelProperty(value = "操作模块", required = true, example = "订单")
-    @NotEmpty(message = "操作模块不能为空")
-    private String module;
-
-    @ApiModelProperty(value = "操作名", required = true, example = "创建订单")
-    @NotEmpty(message = "操作名")
-    private String name;
-
-    @ApiModelProperty(value = "操作分类", required = true, example = "1", notes = "参见 SysOperateLogTypeEnum 枚举类")
-    @NotNull(message = "操作分类不能为空")
-    private Integer type;
-
-    @ApiModelProperty(value = "操作明细", example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。")
-    private String content;
-
-    @ApiModelProperty(value = "拓展字段", example = "{'orderId': 1}")
-    private Map<String, Object> exts;
-
-    @ApiModelProperty(value = "请求方法名", required = true, example = "GET")
-    @NotEmpty(message = "请求方法名不能为空")
-    private String requestMethod;
-
-    @ApiModelProperty(value = "请求地址", required = true, example = "/xxx/yyy")
-    @NotEmpty(message = "请求地址不能为空")
-    private String requestUrl;
-
-    @ApiModelProperty(value = "用户 IP", required = true, example = "127.0.0.1")
-    @NotEmpty(message = "用户 IP 不能为空")
-    private String userIp;
-
-    @ApiModelProperty(value = "浏览器 UserAgent", required = true, example = "Mozilla/5.0")
-    @NotEmpty(message = "浏览器 UserAgent 不能为空")
-    private String userAgent;
-
-    @ApiModelProperty(value = "Java 方法名", required = true, example = "cn.iocoder.yudao.UserController.save(...)")
-    @NotEmpty(message = "Java 方法名不能为空")
-    private String javaMethod;
-
-    @ApiModelProperty(value = "Java 方法的参数")
-    private String javaMethodArgs;
-
-    @ApiModelProperty(value = "开始时间", required = true)
-    @NotNull(message = "开始时间不能为空")
-    private Date startTime;
-
-    @ApiModelProperty(value = "执行时长,单位:毫秒", required = true)
-    @NotNull(message = "执行时长不能为空")
-    private Integer duration;
-
-    @ApiModelProperty(value = "结果码", required = true)
-    @NotNull(message = "结果码不能为空")
-    private Integer resultCode;
-
-    @ApiModelProperty(value = "结果提示")
-    private String resultMsg;
-
-    @ApiModelProperty(value = "结果数据")
-    private String resultData;
-
-}

+ 110 - 0
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java

@@ -0,0 +1,110 @@
+package cn.iocoder.yudao.framework.operatelog.core.service;
+
+import lombok.Data;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 操作日志
+ *
+ * @author 芋道源码
+ */
+@Data
+public class OperateLog {
+
+    /**
+     * 链路追踪编号
+     */
+    private String traceId;
+
+    /**
+     * 用户编号
+     */
+    private Long userId;
+    /**
+     * 用户类型
+     */
+    private Integer userType;
+
+    /**
+     * 操作模块
+     */
+    private String module;
+
+    /**
+     * 操作名
+     */
+    private String name;
+
+    /**
+     * 操作分类
+     */
+    private Integer type;
+
+    /**
+     * 操作明细
+     */
+    private String content;
+
+    /**
+     * 拓展字段
+     */
+    private Map<String, Object> exts;
+
+    /**
+     * 请求方法名
+     */
+    private String requestMethod;
+
+    /**
+     * 请求地址
+     */
+    private String requestUrl;
+
+    /**
+     * 用户 IP
+     */
+    private String userIp;
+
+    /**
+     * 浏览器 UserAgent
+     */
+    private String userAgent;
+
+    /**
+     * Java 方法名
+     */
+    private String javaMethod;
+
+    /**
+     * Java 方法的参数
+     */
+    private String javaMethodArgs;
+
+    /**
+     * 开始时间
+     */
+    private Date startTime;
+
+    /**
+     * 执行时长,单位:毫秒
+     */
+    private Integer duration;
+
+    /**
+     * 结果码
+     */
+    private Integer resultCode;
+
+    /**
+     * 结果提示
+     */
+    private String resultMsg;
+
+    /**
+     * 结果数据
+     */
+    private String resultData;
+
+}

+ 8 - 8
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkService.java

@@ -1,17 +1,17 @@
 package cn.iocoder.yudao.framework.operatelog.core.service;
 
-import cn.iocoder.yudao.framework.operatelog.core.dto.OperateLogCreateReqDTO;
-
-import java.util.concurrent.Future;
-
+/**
+ * 操作日志 Framework Service 接口
+ *
+ * @author 芋道源码
+ */
 public interface OperateLogFrameworkService {
 
     /**
-     * 异步记录操作日志
+     * 记录操作日志
      *
-     * @param reqVO 操作日志请求
-     * @return true: 记录成功,false: 记录失败
+     * @param operateLog 操作日志请求
      */
-    Future<Boolean> createOperateLogAsync(OperateLogCreateReqDTO reqVO);
+    void createOperateLog(OperateLog operateLog);
 
 }

+ 28 - 0
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkServiceImpl.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.framework.operatelog.core.service;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
+import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.scheduling.annotation.Async;
+
+/**
+ * 操作日志 Framework Service 实现类
+ *
+ * 基于 {@link OperateLogApi} 实现,记录操作日志
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+public class OperateLogFrameworkServiceImpl implements OperateLogFrameworkService {
+
+    private final OperateLogApi operateLogApi;
+
+    @Override
+    @Async
+    public void createOperateLog(OperateLog operateLog) {
+        OperateLogCreateReqDTO reqDTO = BeanUtil.copyProperties(operateLog, OperateLogCreateReqDTO.class);
+        operateLogApi.createOperateLog(reqDTO);
+    }
+
+}

+ 2 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayQrPayClient;
 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayWapPayClient;
+import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXLitePayClient;
 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXNativePayClient;
 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient;
@@ -62,7 +63,7 @@ public class PayClientFactoryImpl implements PayClientFactory {
         // TODO @芋艿 WX_LITE WX_APP 如果不添加在 项目启动的时候去初始化会报错无法启动。所以我手动加了两个,具体需要你来配
         switch (channelEnum) {
             case WX_PUB: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);
-            case WX_LITE: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);
+            case WX_LITE: return (AbstractPayClient<Config>) new WXLitePayClient(channelId, (WXPayClientConfig) config); //微信小程序请求支付
             case WX_APP: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);
             case WX_NATIVE: return (AbstractPayClient<Config>) new WXNativePayClient(channelId, (WXPayClientConfig) config);
             case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config);

+ 13 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.http.HttpUtil;
 import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
 import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
 import cn.iocoder.yudao.framework.pay.core.client.dto.*;
@@ -130,11 +131,20 @@ public abstract class AbstractAlipayClient extends AbstractPayClient<AlipayPayCl
         }
     }
 
+
+
+    /**
+     * 支付宝统一回调参数  str 转 map
+     *
+     * @param s 支付宝支付通知回调参数
+     * @return map 支付宝集合
+     */
     public static Map<String, String> strToMap(String s) {
+        // TODO @zxy:这个可以使用 hutool 的 HttpUtil decodeParams 方法么?
         Map<String, String> stringStringMap = new HashMap<>();
-        //调整时间格式
+        // 调整时间格式
         String s3 = s.replaceAll("%3A", ":");
-        //获取map
+        // 获取 map
         String s4 = s3.replace("+", " ");
         String[] split = s4.split("&");
         for (String s1 : split) {
@@ -143,4 +153,5 @@ public abstract class AbstractAlipayClient extends AbstractPayClient<AlipayPayCl
         }
         return stringStringMap;
     }
+
 }

+ 203 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java

@@ -0,0 +1,203 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.io.FileUtils;
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
+import cn.iocoder.yudao.framework.pay.core.client.dto.*;
+import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
+import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
+import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
+import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
+import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Objects;
+
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
+import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS;
+import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS;
+
+
+/**
+ * 微信小程序下支付
+ *
+ * @author zwy
+ */
+@Slf4j
+public class WXLitePayClient extends AbstractPayClient<WXPayClientConfig> {
+
+    private WxPayService client;
+
+    public WXLitePayClient(Long channelId, WXPayClientConfig config) {
+        super(channelId, PayChannelEnum.WX_LITE.getCode(), config, new WXCodeMapping());
+    }
+
+    @Override
+    protected void doInit() {
+        WxPayConfig payConfig = new WxPayConfig();
+        BeanUtil.copyProperties(config, payConfig, "keyContent");
+        payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式
+//        if (StrUtil.isNotEmpty(config.getKeyContent())) {
+//            payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8));
+//        }
+        if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
+            // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
+            payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
+        }
+        if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
+            // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
+            payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
+        }
+        // 真实客户端
+        this.client = new WxPayServiceImpl();
+        client.setConfig(payConfig);
+    }
+
+    @Override
+    public PayCommonResult<WxPayMpOrderResult> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
+        WxPayMpOrderResult response;
+        try {
+            switch (config.getApiVersion()) {
+                case WXPayClientConfig.API_VERSION_V2:
+                    response = this.unifiedOrderV2(reqDTO);
+                    break;
+                case WXPayClientConfig.API_VERSION_V3:
+                    WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO);
+                    // 将 V3 的结果,统一转换成 V2。返回的字段是一致的
+                    response = new WxPayMpOrderResult();
+                    BeanUtil.copyProperties(responseV3, response, true);
+                    break;
+                default:
+                    throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+            }
+        } catch (WxPayException e) {
+            log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e);
+            return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"),
+                    ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()), null, codeMapping);
+        }
+        return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping);
+    }
+
+    private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 构建 WxPayUnifiedOrderRequest 对象
+        WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
+                .outTradeNo(reqDTO.getMerchantOrderId())
+                .body(reqDTO.getBody())
+                .totalFee(reqDTO.getAmount().intValue()) // 单位分
+                .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")) // v2的时间格式
+                .spbillCreateIp(reqDTO.getUserIp())
+                .openid(getOpenid(reqDTO))
+                .notifyUrl(reqDTO.getNotifyUrl())
+                .build();
+        // 执行请求
+        return client.createOrder(request);
+    }
+
+    private WxPayUnifiedOrderV3Result.JsapiResult unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 构建 WxPayUnifiedOrderRequest 对象
+        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
+        request.setOutTradeNo(reqDTO.getMerchantOrderId());
+
+        request.setDescription(reqDTO.getBody());
+        request.setAmount(new WxPayUnifiedOrderV3Request
+                .Amount()
+                .setTotal(reqDTO
+                        .getAmount()
+                        .intValue())); // 单位分
+        request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")); // v3的时间格式
+        request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
+        request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
+        request.setNotifyUrl(reqDTO.getNotifyUrl());
+        // 执行请求
+        return client.createOrderV3(TradeTypeEnum.JSAPI, request);
+    }
+
+    private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) {
+        String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid");
+        if (StrUtil.isEmpty(openid)) {
+            throw new IllegalArgumentException("支付请求的 openid 不能为空!");
+        }
+        return openid;
+    }
+
+    /**
+     *
+     * 微信支付回调 分 v2 和v3 的处理方式
+     *
+     * @param data 通知结果
+     * @return 支付回调对象
+     * @throws WxPayException 微信异常类
+     */
+    @Override
+    public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException {
+        log.info("[parseOrderNotify][微信支付回调data数据:{}]", data.getBody());
+        // 微信支付 v2 回调结果处理
+        switch (config.getApiVersion()) {
+            case WXPayClientConfig.API_VERSION_V2:
+                return parseOrderNotifyV2(data);
+            case WXPayClientConfig.API_VERSION_V3:
+                return parseOrderNotifyV3(data);
+            default:
+                throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+        }
+    }
+
+    private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyDataDTO data) throws WxPayException {
+        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);
+        WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
+        // 转换结果
+        Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),
+                "支付结果非 SUCCESS");
+
+        return PayOrderNotifyRespDTO
+                .builder()
+                .orderExtensionNo(result.getOutTradeNo())
+                .channelOrderNo(result.getTradeState())
+                .successTime(DateUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
+                .data(data.getBody())
+                .build();
+    }
+
+    private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyDataDTO data) throws WxPayException {
+        WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
+        Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
+        // 转换结果
+        return PayOrderNotifyRespDTO
+                .builder()
+                .orderExtensionNo(notifyResult.getOutTradeNo())
+                .channelOrderNo(notifyResult.getTransactionId())
+                .channelUserId(notifyResult.getOpenid())
+                .successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
+                .data(data.getBody())
+                .build();
+
+    }
+
+    @Override
+    public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) {
+        //TODO 需要实现
+        throw new UnsupportedOperationException("需要实现");
+    }
+
+
+    @Override
+    protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
+        //TODO 需要实现
+        throw new UnsupportedOperationException();
+    }
+
+}

+ 58 - 13
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXNativePayClient.java

@@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.*;
 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
@@ -28,9 +29,14 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString
 import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS;
 import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS;
 
-
+/**
+ * 微信 App 支付
+ *
+ * @author zwy
+ */
 @Slf4j
 public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> {
+
     private WxPayService client;
 
     public WXNativePayClient(Long channelId, WXPayClientConfig config) {
@@ -61,7 +67,7 @@ public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> {
     @Override
     public PayCommonResult<String> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
         // 这里原生的返回的是支付的 url 所以直接使用string接收
-        //"invokeResponse": "weixin://wxpay/bizpayurl?pr=EGYAem7zz"
+        // "invokeResponse": "weixin://wxpay/bizpayurl?pr=EGYAem7zz"
         String responseV3;
         try {
             switch (config.getApiVersion()) {
@@ -84,7 +90,7 @@ public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> {
 
     private WxPayNativeOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         //前端
-        String trade_type = reqDTO.getChannelExtras().get("trade_type");
+        String tradeType = reqDTO.getChannelExtras().get("trade_type");
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest
                 .newBuilder()
@@ -94,7 +100,7 @@ public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> {
                 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
                 .spbillCreateIp(reqDTO.getUserIp())
                 .notifyUrl(reqDTO.getNotifyUrl())
-                .productId(trade_type)
+                .productId(tradeType)
                 .build();
         // 执行请求
         return client.createOrder(request);
@@ -109,33 +115,72 @@ public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> {
         request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
         request.setNotifyUrl(reqDTO.getNotifyUrl());
         // 执行请求
-//        log.info("支付字段request:{}",request.getTimeExpire());
-
         return client.createOrderV3(TradeTypeEnum.NATIVE, request);
     }
 
-
+    /**
+     *
+     * 微信支付回调 分v2 和v3 的处理方式
+     *
+     * @param data 通知结果
+     * @return 支付回调对象
+     * @throws WxPayException 微信异常类
+     */
     @Override
     public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException {
+        log.info("微信支付回调data数据:{}", data.getBody());
+        // 微信支付 v2 回调结果处理
+        switch (config.getApiVersion()) {
+            case WXPayClientConfig.API_VERSION_V2:
+                return parseOrderNotifyV2(data);
+            case WXPayClientConfig.API_VERSION_V3:
+                return parseOrderNotifyV3(data);
+            default:
+                throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+        }
+    }
+
+    private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyDataDTO data) throws WxPayException {
+        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);
+        WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
+        // 转换结果
+        Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),
+                "支付结果非 SUCCESS");
+        return PayOrderNotifyRespDTO
+                .builder()
+                .orderExtensionNo(result.getOutTradeNo())
+                .channelOrderNo(result.getTradeState())
+                .successTime(DateUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
+                .data(data.getBody())
+                .build();
+    }
+
+    private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyDataDTO data) throws WxPayException {
         WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
         Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
         // 转换结果
-        return PayOrderNotifyRespDTO.builder().orderExtensionNo(notifyResult.getOutTradeNo())
-                .channelOrderNo(notifyResult.getTransactionId()).channelUserId(notifyResult.getOpenid())
-                .successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
-                .data(data.getBody()).build();
+        return PayOrderNotifyRespDTO
+                .builder()
+                .orderExtensionNo(notifyResult.getOutTradeNo())
+                .channelOrderNo(notifyResult.getTransactionId())
+                .channelUserId(notifyResult.getOpenid())
+                .successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
+                .data(data.getBody())
+                .build();
+
     }
 
     @Override
     public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) {
-        //TODO 需要实现
+        // TODO 需要实现
         throw new UnsupportedOperationException("需要实现");
     }
 
 
     @Override
     protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
-        //TODO 需要实现
+        // TODO 需要实现
         throw new UnsupportedOperationException();
     }
+
 }

+ 0 - 3
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java

@@ -11,8 +11,6 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.util.Set;
 
-// TODO 芋艿:参数校验
-
 /**
  * 微信支付的 PayClientConfig 实现类
  * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性
@@ -22,7 +20,6 @@ import java.util.Set;
 @Data
 public class WXPayClientConfig implements PayClientConfig {
 
-    // TODO 芋艿:V2 or V3 客户端
     /**
      * API 版本 - V2
      * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1

+ 47 - 8
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java

@@ -12,6 +12,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.*;
 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
@@ -95,7 +96,6 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
                 .outTradeNo(reqDTO.getMerchantOrderId())
-                // TODO 芋艿:貌似没 title?
                 .body(reqDTO.getBody())
                 .totalFee(reqDTO.getAmount().intValue()) // 单位分
                 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
@@ -111,7 +111,6 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
         request.setOutTradeNo(reqDTO.getMerchantOrderId());
-        // TODO 芋艿:貌似没 title?
         request.setDescription(reqDTO.getBody());
         request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分
         request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"));
@@ -130,27 +129,67 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
         return openid;
     }
 
+    /**
+     *
+     * 微信支付回调 分v2 和v3 的处理方式
+     *
+     * @param data 通知结果
+     * @return 支付回调对象
+     * @throws WxPayException 微信异常类
+     */
     @Override
     public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException {
+        log.info("[parseOrderNotify][微信支付回调data数据: {}]", data.getBody());
+        // 微信支付 v2 回调结果处理
+        switch (config.getApiVersion()) {
+            case WXPayClientConfig.API_VERSION_V2:
+                return parseOrderNotifyV2(data);
+            case WXPayClientConfig.API_VERSION_V3:
+                return parseOrderNotifyV3(data);
+            default:
+                throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+        }
+    }
+
+    private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyDataDTO data) throws WxPayException {
+        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);
+        WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
+        // 转换结果
+        Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),
+                "支付结果非 SUCCESS");
+        return PayOrderNotifyRespDTO
+                .builder()
+                .orderExtensionNo(result.getOutTradeNo())
+                .channelOrderNo(result.getTradeState())
+                .successTime(DateUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
+                .data(data.getBody())
+                .build();
+    }
+
+    private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyDataDTO data) throws WxPayException {
         WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
         Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
         // 转换结果
-        return PayOrderNotifyRespDTO.builder().orderExtensionNo(notifyResult.getOutTradeNo())
-                .channelOrderNo(notifyResult.getTransactionId()).channelUserId(notifyResult.getOpenid())
+        return PayOrderNotifyRespDTO
+                .builder()
+                .orderExtensionNo(notifyResult.getOutTradeNo())
+                .channelOrderNo(notifyResult.getTransactionId())
+                .channelUserId(notifyResult.getOpenid())
                 .successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
-                .data(data.getBody()).build();
+                .data(data.getBody())
+                .build();
+
     }
 
     @Override
     public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) {
-        //TODO 需要实现
+        // TODO 需要实现
         throw new UnsupportedOperationException("需要实现");
     }
 
-
     @Override
     protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
-        //TODO 需要实现
+        // TODO 需要实现
         throw new UnsupportedOperationException();
     }
 

+ 0 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java

@@ -22,7 +22,6 @@ public enum PayChannelEnum {
     WX_APP("wx_app", "微信 App 支付", WXPayClientConfig.class),
     WX_NATIVE("wx_native", "微信 native 支付", WXPayClientConfig.class),
 
-
     ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付", AlipayPayClientConfig.class),
     ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付", AlipayPayClientConfig.class),
     ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class),

+ 6 - 0
yudao-framework/yudao-spring-boot-starter-biz-tenant/pom.xml

@@ -56,6 +56,12 @@
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <!-- 工具类相关 -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 7 - 0
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java

@@ -11,9 +11,11 @@ import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator;
 import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
 import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
 import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
+import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkServiceImpl;
 import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
 import cn.iocoder.yudao.framework.web.config.WebProperties;
 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
+import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
 import org.springframework.beans.BeansException;
@@ -29,6 +31,11 @@ import org.springframework.context.annotation.Configuration;
 @EnableConfigurationProperties(TenantProperties.class)
 public class YudaoTenantAutoConfiguration {
 
+    @Bean
+    public TenantFrameworkService tenantFrameworkService(TenantApi tenantApi) {
+        return new TenantFrameworkServiceImpl(tenantApi);
+    }
+
     // ========== AOP ==========
 
     @Bean

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java

@@ -5,6 +5,8 @@ import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor;
 import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 
+import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
+
 /**
  * 多租户 {@link AbstractRedisMessage} 拦截器
  *
@@ -15,8 +17,6 @@ import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  */
 public class TenantRedisMessageInterceptor implements RedisMessageInterceptor {
 
-    private static final String HEADER_TENANT_ID = "tenant-id";
-
     @Override
     public void sendMessageBefore(AbstractRedisMessage message) {
         Long tenantId = TenantContextHolder.getTenantId();

+ 73 - 0
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkServiceImpl.java

@@ -0,0 +1,73 @@
+package cn.iocoder.yudao.framework.tenant.core.service;
+
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
+import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+
+import java.time.Duration;
+import java.util.List;
+
+/**
+ * Tenant 框架 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+public class TenantFrameworkServiceImpl implements TenantFrameworkService {
+
+    private static final ServiceException SERVICE_EXCEPTION_NULL = new ServiceException();
+
+    private final TenantApi tenantApi;
+
+    /**
+     * 针对 {@link #getTenantIds()} 的缓存
+     */
+    private final LoadingCache<Object, List<Long>> getTenantIdsCache = CacheUtils.buildAsyncReloadingCache(
+            Duration.ofMinutes(1L), // 过期时间 1 分钟
+            new CacheLoader<Object, List<Long>>() {
+
+                @Override
+                public List<Long> load(Object key) {
+                    return tenantApi.getTenantIds();
+                }
+
+            });
+
+    /**
+     * 针对 {@link #validTenant(Long)} 的缓存
+     */
+    private final LoadingCache<Long, ServiceException> validTenantCache = CacheUtils.buildAsyncReloadingCache(
+            Duration.ofMinutes(1L), // 过期时间 1 分钟
+            new CacheLoader<Long, ServiceException>() {
+
+                @Override
+                public ServiceException load(Long id) {
+                    try {
+                        tenantApi.validTenant(id);
+                        return SERVICE_EXCEPTION_NULL;
+                    } catch (ServiceException ex) {
+                        return ex;
+                    }
+                }
+
+            });
+
+    @Override
+    @SneakyThrows
+    public List<Long> getTenantIds() {
+        return getTenantIdsCache.get(Boolean.TRUE);
+    }
+
+    @Override
+    public void validTenant(Long id) {
+        ServiceException serviceException = validTenantCache.getUnchecked(id);
+        if (serviceException != SERVICE_EXCEPTION_NULL) {
+            throw serviceException;
+        }
+    }
+
+}

+ 0 - 2
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantContextWebFilter.java

@@ -18,8 +18,6 @@ import java.io.IOException;
  */
 public class TenantContextWebFilter extends OncePerRequestFilter {
 
-    private static final String HEADER_TENANT_ID = "tenant-id";
-
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
             throws ServletException, IOException {

+ 7 - 8
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.framework.excel.core.convert;
 
 import cn.hutool.core.convert.Convert;
-import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
 import com.alibaba.excel.converters.Converter;
@@ -12,7 +11,7 @@ import com.alibaba.excel.metadata.property.ExcelContentProperty;
 import lombok.extern.slf4j.Slf4j;
 
 /**
- * Excel {@link DictDataRespDTO} 数据字典转换器
+ * Excel 数据字典转换器
  *
  * @author 芋道源码
  */
@@ -35,14 +34,14 @@ public class DictConvert implements Converter<Object> {
         // 使用字典解析
         String type = getType(contentProperty);
         String label = cellData.getStringValue();
-        DictDataRespDTO dictData = DictFrameworkUtils.parseDictDataFromCache(type, label);
-        if (dictData == null) {
+        String value = DictFrameworkUtils.parseDictDataValue(type, label);
+        if (value == null) {
             log.error("[convertToJavaData][type({}) 解析不掉 label({})]", type, label);
             return null;
         }
         // 将 String 的 value 转换成对应的属性
         Class<?> fieldClazz = contentProperty.getField().getType();
-        return Convert.convert(fieldClazz, dictData.getValue());
+        return Convert.convert(fieldClazz, value);
     }
 
     @Override
@@ -56,13 +55,13 @@ public class DictConvert implements Converter<Object> {
         // 使用字典格式化
         String type = getType(contentProperty);
         String value = String.valueOf(object);
-        DictDataRespDTO dictData = DictFrameworkUtils.getDictDataFromCache(type, value);
-        if (dictData == null) {
+        String label = DictFrameworkUtils.getDictDataLabel(type, value);
+        if (label == null) {
             log.error("[convertToExcelData][type({}) 转换不了 label({})]", type, value);
             return new CellData<>("");
         }
         // 生成 Excel 小表格
-        return new CellData<>(dictData.getLabel());
+        return new CellData<>(label);
     }
 
     private static String getType(ExcelContentProperty contentProperty) {

+ 0 - 62
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/config/YudaoExtensionAutoConfiguration.java

@@ -1,62 +0,0 @@
-package cn.iocoder.yudao.framework.extension.config;
-
-import cn.iocoder.yudao.framework.extension.core.ExtensionBootstrap;
-import cn.iocoder.yudao.framework.extension.core.context.ExtensionContext;
-import cn.iocoder.yudao.framework.extension.core.context.ExtensionContextHolder;
-import cn.iocoder.yudao.framework.extension.core.context.ExtensionExecutor;
-import cn.iocoder.yudao.framework.extension.core.factory.ExtensionFactory;
-import cn.iocoder.yudao.framework.extension.core.factory.ExtensionRegisterFactory;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * @description 扩展点组件自动装配
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 21:50
- * @class cn.iocoder.yudao.framework.extension.config.YudaoExtensionAutoConfiguration.java
- */
-@Configuration
-public class YudaoExtensionAutoConfiguration {
-
-    /**
-     * 组件初始化
-     * @return
-     */
-    @Bean(initMethod = "init")
-    @ConditionalOnMissingBean(ExtensionBootstrap.class)
-    public ExtensionBootstrap bootstrap() {
-        return new ExtensionBootstrap();
-    }
-
-    /**
-     * 扩展点工厂
-     * @return
-     */
-    @Bean
-    @ConditionalOnMissingBean({ExtensionRegisterFactory.class, ExtensionFactory.class})
-    public ExtensionRegisterFactory registerFactory() {
-        return new ExtensionRegisterFactory();
-    }
-
-    /**
-     * 扩展组件上下文对象
-     * @return
-     */
-    @Bean
-    @ConditionalOnMissingBean({ExtensionContextHolder.class, ExtensionContext.class})
-    public ExtensionContextHolder context() {
-        return new ExtensionContextHolder();
-    }
-
-    /**
-     * 扩展组件执行器
-     * @return
-     */
-    @Bean
-    @ConditionalOnMissingBean(ExtensionExecutor.class)
-    public ExtensionExecutor executor() {
-        return new ExtensionExecutor();
-    }
-}

+ 0 - 142
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/BusinessScenario.java

@@ -1,142 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core;
-
-import javax.validation.constraints.NotNull;
-import java.io.Serializable;
-import java.util.StringJoiner;
-
-/**
- * @description 业务场景 = businessId + useCase + scenario, 用来标识系统中唯一的一个场景<br/>
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 22:19
- * @class cn.iocoder.yudao.framework.extension.core.BusinessScenario.java
- */
-public class BusinessScenario implements Serializable {
-
-    /**
-     * 默认业务id
-     */
-    public final static String DEFAULT_BUSINESS_ID = "#defaultBusinessId#";
-
-    /**
-     * 默认用例
-     */
-    public final static String DEFAULT_USECASE = "#defaultUseCase#";
-
-    /**
-     * 默认场景
-     */
-    public final static String DEFAULT_SCENARIO = "#defaultScenario#";
-
-    /**
-     * 分隔符
-     */
-    private final static String DOT_SEPARATOR = ".";
-
-    /**
-     * 业务Id
-     */
-    private String businessId;
-
-    /**
-     * 用例
-     */
-    private String useCase;
-
-    /**
-     * 场景
-     */
-    private String scenario;
-
-    public BusinessScenario() {
-        this.businessId = DEFAULT_BUSINESS_ID;
-        this.useCase = DEFAULT_USECASE;
-        this.scenario = DEFAULT_SCENARIO;
-    }
-
-    public BusinessScenario(@NotNull String businessId, @NotNull String useCase, @NotNull String scenario) {
-        this.businessId = businessId;
-        this.useCase = useCase;
-        this.scenario = scenario;
-    }
-
-    public BusinessScenario(@NotNull String scenario) {
-        this();
-        this.scenario = scenario;
-    }
-
-    public BusinessScenario(@NotNull String useCase, @NotNull String scenario) {
-        this(DEFAULT_BUSINESS_ID, useCase, scenario);
-    }
-
-    public String getBusinessId() {
-        return businessId;
-    }
-
-    public void setBusinessId(String businessId) {
-        this.businessId = businessId;
-    }
-
-    public String getUseCase() {
-        return useCase;
-    }
-
-    public void setUseCase(String useCase) {
-        this.useCase = useCase;
-    }
-
-    public String getScenario() {
-        return scenario;
-    }
-
-    public void setScenario(String scenario) {
-        this.scenario = scenario;
-    }
-
-    /**
-     * 构建业务场景
-     * @param businessId
-     * @param useCase
-     * @param scenario
-     * @return
-     */
-    public static BusinessScenario valueOf(@NotNull String businessId, @NotNull String useCase, @NotNull String scenario) {
-        return new BusinessScenario(businessId, useCase, scenario);
-    }
-
-    /**
-     * 构建业务场景
-     * @param useCase
-     * @param scenario
-     * @return
-     */
-    public static BusinessScenario valueOf(@NotNull String useCase, @NotNull String scenario) {
-        return new BusinessScenario(useCase, scenario);
-    }
-
-    /**
-     * 构建业务场景
-     * @param scenario
-     * @return
-     */
-    public static BusinessScenario valueOf(@NotNull String scenario) {
-        return new BusinessScenario(scenario);
-    }
-
-    /**
-     * 业务场景唯一标识
-     * @return
-     */
-    public String getUniqueIdentity(){
-        return new StringJoiner(DOT_SEPARATOR).add(businessId).add(useCase).add(scenario).toString();
-    }
-
-    @Override
-    public String toString() {
-        return "BusinessScenario{" +
-                "businessId='" + businessId + '\'' +
-                ", useCase='" + useCase + '\'' +
-                ", scenario='" + scenario + '\'' +
-                '}';
-    }
-}

+ 0 - 41
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/ExtensionBootstrap.java

@@ -1,41 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core;
-
-import cn.iocoder.yudao.framework.extension.core.factory.ExtensionRegisterFactory;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-
-import javax.annotation.PostConstruct;
-
-/**
- * @description 
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-29 00:18
- * @class cn.iocoder.yudao.framework.extension.core.ExtensionBootstrap.java
- */
-public class ExtensionBootstrap implements ApplicationContextAware {
-
-    /**
-     * spring 容器
-     */
-    private ApplicationContext applicationContext;
-
-    @Autowired
-    private ExtensionRegisterFactory registerFactory;
-
-    /**
-     * 初始化
-     */
-    @PostConstruct
-    public void init() {
-        registerFactory.setApplicationContext(applicationContext);
-        registerFactory.register(null);
-    }
-
-    @Override
-    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-        this.applicationContext = applicationContext;
-    }
-}

+ 0 - 131
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/AbstractComponentExecutor.java

@@ -1,131 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core.context;
-
-import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
-import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
-
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/**
- * @description 执行器通用方法
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-29 00:38
- * @class cn.iocoder.yudao.framework.extension.core.context.AbstractComponentExecutor.java
- */
-public abstract class AbstractComponentExecutor {
-
-    /**
-     * ("业务" + "用例" + "场景")执行扩展组件,并返回执行结果
-     * @param targetClazz
-     * @param businessId
-     * @param useCase
-     * @param scenario
-     * @param function
-     * @param <R>
-     * @param <T>
-     * @return
-     */
-    public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, String businessId, String useCase, String scenario, Function<T, R> function) {
-        return execute(targetClazz, BusinessScenario.valueOf(businessId, useCase, scenario), function);
-    }
-
-
-    /**
-     * ("用例" + "场景")执行扩展组件,并返回执行结果
-     * @param targetClazz
-     * @param useCase
-     * @param scenario
-     * @param function
-     * @param <R>
-     * @param <T>
-     * @return
-     */
-    public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, String useCase, String scenario, Function<T, R> function) {
-        return execute(targetClazz, BusinessScenario.valueOf(useCase, scenario), function);
-    }
-
-    /**
-     * ("场景")执行扩展组件,并返回执行结果
-     * @param targetClazz
-     * @param scenario
-     * @param function
-     * @param <R>
-     * @param <T>
-     * @return
-     */
-    public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, String scenario, Function<T, R> function) {
-        return execute(targetClazz, BusinessScenario.valueOf(scenario), function);
-    }
-
-    /**
-     * 执行扩展组件,并返回执行结果
-     * @param targetClazz
-     * @param businessScenario
-     * @param function
-     * @param <R> Response Type
-     * @param <T> Parameter Type
-     * @return
-     */
-    public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, BusinessScenario businessScenario, Function<T, R> function) {
-        T component = locateComponent(targetClazz, businessScenario);
-        return function.apply(component);
-    }
-
-    /**
-     * ("业务" + "用例" + "场景")执行扩展组件,适用于无返回值的业务
-     * @param targetClazz
-     * @param businessId
-     * @param useCase
-     * @param scenario
-     * @param consumer
-     * @param <T>
-     */
-    public <T extends ExtensionPoint> void accept(Class<T> targetClazz, String businessId, String useCase, String scenario, Consumer<T> consumer) {
-        accept(targetClazz, BusinessScenario.valueOf(businessId, useCase, scenario), consumer);
-    }
-
-    /**
-     * ("场景")执行扩展组件,适用于无返回值的业务
-     * @param targetClazz
-     * @param useCase
-     * @param scenario
-     * @param consumer
-     * @param <T>
-     */
-    public <T extends ExtensionPoint> void accept(Class<T> targetClazz, String useCase, String scenario, Consumer<T> consumer) {
-        accept(targetClazz, BusinessScenario.valueOf(useCase, scenario), consumer);
-    }
-
-    /**
-     * ("场景")执行扩展组件,适用于无返回值的业务
-     * @param targetClazz
-     * @param scenario
-     * @param consumer
-     * @param <T>
-     */
-    public <T extends ExtensionPoint> void accept(Class<T> targetClazz, String scenario, Consumer<T> consumer) {
-        accept(targetClazz, BusinessScenario.valueOf(scenario), consumer);
-    }
-
-    /**
-     * 执行扩展组件,适用于无返回值的业务
-     * @param targetClazz
-     * @param businessScenario
-     * @param consumer
-     * @param <T> Parameter Type
-     */
-    public <T extends ExtensionPoint> void accept(Class<T> targetClazz, BusinessScenario businessScenario, Consumer<T> consumer) {
-        T component = locateComponent(targetClazz, businessScenario);
-        consumer.accept(component);
-    }
-
-    /**
-     * 获取/定位扩展点组件
-     * @param targetClazz
-     * @param businessScenario
-     * @param <C>
-     * @return
-     */
-    protected abstract <C extends ExtensionPoint> C locateComponent(Class<C> targetClazz, BusinessScenario businessScenario);
-}

+ 0 - 56
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/ExtensionContext.java

@@ -1,56 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core.context;
-
-import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
-import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
-
-/**
- * @description 上下文,包含各个扩展点的相关操作
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 22:15
- * @class cn.iocoder.yudao.framework.extension.core.context.ExtensionContext.java
- */
-public interface ExtensionContext {
-
-    /**
-     * 根据业务场景唯一标识获取扩展点组件实现类
-     * @param businessId
-     * @param useCase
-     * @param scenario
-     * @param clazz
-     * @param <T>
-     * @return
-     */
-    <T extends ExtensionPoint> T getPoint(String businessId, String useCase, String scenario, Class<T> clazz);
-
-    /**
-     * 根据("实例" + "场景")获取扩展点组件实现类,其中:业务id(businessId)= {@linkplain cn.iocoder.yudao.framework.extension.core.BusinessScenario.DEFAULT_BUSINESS_ID}
-     * @param useCase
-     * @param scenario
-     * @param clazz
-     * @param <T>
-     * @return
-     */
-    <T extends ExtensionPoint> T getPoint(String useCase, String scenario, Class<T> clazz);
-
-    /**
-     * 根据("场景")获取扩展点组件实现类 <br/>
-     * 其中:
-     *    业务id(businessId)= {@linkplain cn.iocoder.yudao.framework.extension.core.BusinessScenario.DEFAULT_BUSINESS_ID}
-     *    实例(useCase)= {@linkplain cn.iocoder.yudao.framework.extension.core.BusinessScenario.DEFAULT_USECASE}
-     * @param scenario
-     * @param clazz
-     * @param <T>
-     * @return
-     */
-    <T extends ExtensionPoint> T getPoint(String scenario, Class<T> clazz);
-
-    /**
-     * 根据业务场景唯一标识获取扩展点组件实现类
-     * @param businessScenario
-     * @param clazz
-     * @param <T>
-     * @return
-     */
-    <T extends ExtensionPoint> T getPoint(BusinessScenario businessScenario, Class<T> clazz);
-}

+ 0 - 45
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/ExtensionContextHolder.java

@@ -1,45 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core.context;
-
-import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
-import cn.iocoder.yudao.framework.extension.core.factory.ExtensionFactory;
-import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import javax.validation.constraints.NotNull;
-
-/**
- * @description 上下文及扩展点组件工厂的持有类
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-29 00:29
- * @class cn.iocoder.yudao.framework.extension.core.context.ExtensionContextHolder.java
- */
-@Component
-@Slf4j
-public class ExtensionContextHolder implements ExtensionContext{
-
-    @Autowired
-    private ExtensionFactory factory;
-
-    @Override
-    public <T extends ExtensionPoint> T getPoint(@NotNull String businessId, @NotNull String useCase, @NotNull String scenario, Class<T> clazz) {
-        return getPoint(BusinessScenario.valueOf(businessId, useCase, scenario), clazz);
-    }
-
-    @Override
-    public <T extends ExtensionPoint> T getPoint(@NotNull String useCase, String scenario, Class<T> clazz) {
-        return getPoint(BusinessScenario.valueOf(useCase, scenario), clazz);
-    }
-
-    @Override
-    public <T extends ExtensionPoint> T getPoint(@NotNull String scenario, Class<T> clazz) {
-        return getPoint(BusinessScenario.valueOf(scenario), clazz);
-    }
-
-    @Override
-    public <T extends ExtensionPoint> T getPoint(@NotNull BusinessScenario businessScenario, Class<T> clazz) {
-        return factory.get(businessScenario, clazz);
-    }
-}

+ 0 - 28
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/ExtensionExecutor.java

@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core.context;
-
-import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
-import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-/**
- * @description 扩展组件执行器
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-29 00:32
- * @class cn.iocoder.yudao.framework.extension.core.context.ExtensionExecutor.java
- */
-@Component
-@Slf4j
-public class ExtensionExecutor extends AbstractComponentExecutor{
-
-    @Autowired
-    private ExtensionContextHolder contextHolder;
-
-
-    @Override
-    protected <C extends ExtensionPoint> C locateComponent(Class<C> targetClazz, BusinessScenario businessScenario) {
-        return contextHolder.getPoint(businessScenario, targetClazz);
-    }
-}

+ 0 - 96
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/factory/ExtensionDefinition.java

@@ -1,96 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core.factory;
-
-import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
-import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
-import lombok.Getter;
-import lombok.Setter;
-
-import javax.validation.constraints.NotNull;
-import java.io.Serializable;
-import java.util.Objects;
-
-/**
- * @description 扩展定义(扩展坐标),标识唯一一个业务场景实现
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 23:14
- * @class cn.iocoder.yudao.framework.extension.core.factory.ExtensionDefinition.java
- */
-@Setter
-@Getter
-public class ExtensionDefinition implements Serializable {
-
-    /**
-     * 业务场景唯一标识(id)
-     */
-    private String uniqueIdentify;
-
-    /**
-     * 扩展点实现类名称
-     */
-    private String extensionPointName;
-
-    /**
-     * 业务场景
-     */
-    private BusinessScenario businessScenario;
-
-    /**
-     * 扩展点实现类
-     */
-    private ExtensionPoint extensionPoint;
-
-    /**
-     * class
-     */
-    private Class extensionPointClass;
-
-    public ExtensionDefinition() {
-    }
-
-    public ExtensionDefinition(@NotNull BusinessScenario businessScenario, @NotNull ExtensionPoint extensionPoint) {
-        this.businessScenario = businessScenario;
-        this.extensionPoint = extensionPoint;
-        this.uniqueIdentify = this.businessScenario.getUniqueIdentity();
-        this.extensionPointClass = this.extensionPoint.getClass();
-    }
-
-    /**
-     * 构建definition
-     * @param businessScenario
-     * @param point
-     * @return
-     */
-    public static ExtensionDefinition valueOf(@NotNull BusinessScenario businessScenario, @NotNull ExtensionPoint point) {
-        return new ExtensionDefinition(businessScenario, point);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        ExtensionDefinition that = (ExtensionDefinition) o;
-        return Objects.equals(uniqueIdentify, that.uniqueIdentify) && Objects.equals(extensionPointName, that.extensionPointName);
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((uniqueIdentify == null) ? 0 : uniqueIdentify.hashCode());
-        result = prime * result + ((extensionPointName == null) ? 0 : extensionPointName.hashCode());
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "ExtensionDefinition{" +
-                "uniqueIdentify='" + uniqueIdentify + '\'' +
-                ", extensionPointName='" + extensionPointName + '\'' +
-                '}';
-    }
-}

+ 0 - 29
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/factory/ExtensionFactory.java

@@ -1,29 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core.factory;
-
-import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
-import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
-
-/**
- * @description 扩展点工厂
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 23:04
- * @class cn.iocoder.yudao.framework.extension.core.factory.ExtensionFactory.java
- */
-public interface ExtensionFactory {
-
-    /**
-     * 注册所有扩展点实现类
-     * @param basePackage
-     */
-    void register(String basePackage);
-
-    /**
-     * 根据业务场景获取指定类型的扩展点
-     * @param businessScenario
-     * @param clazz
-     * @param <T>
-     * @return
-     */
-    <T extends ExtensionPoint> T get(BusinessScenario businessScenario, Class<T> clazz);
-}

+ 0 - 86
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/factory/ExtensionRegisterFactory.java

@@ -1,86 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core.factory;
-
-import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
-import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
-import cn.iocoder.yudao.framework.extension.core.stereotype.Extension;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.aop.support.AopUtils;
-import org.springframework.context.ApplicationContext;
-import org.springframework.core.annotation.AnnotationUtils;
-import org.springframework.stereotype.Component;
-import org.springframework.util.ClassUtils;
-
-import javax.validation.constraints.NotNull;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * @description 注册工厂
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 23:07
- * @class cn.iocoder.yudao.framework.extension.core.factory.ExtensionRegisterFactory.java
- */
-@Component
-@Slf4j
-public class ExtensionRegisterFactory implements ExtensionFactory {
-
-    /**
-     * spring ApplicationContext
-     */
-    private ApplicationContext applicationContext;
-
-    /**
-     * 扩展点实现类集合
-     */
-    private Map<String, ExtensionDefinition> registerExtensionBeans = new ConcurrentHashMap<>();
-
-    @Override
-    public void register(String basePackage) {
-        final Map<String, Object> beans = applicationContext.getBeansWithAnnotation(Extension.class);
-        if(beans == null || beans.isEmpty()) {
-            return;
-        }
-
-        beans.values().forEach(point -> doRegister((ExtensionPoint) point));
-        log.info("业务场景相关扩展点注册完成,注册数量: {}", registerExtensionBeans.size());
-    }
-
-    @Override
-    public <T extends ExtensionPoint> T get(BusinessScenario businessScenario, Class<T> clazz) {
-
-        final ExtensionDefinition definition = registerExtensionBeans.get(businessScenario.getUniqueIdentity());
-        if(definition == null) {
-            log.error("获取业务场景扩展点实现失败,失败原因:尚未定义该业务场景相关扩展点。{}", businessScenario);
-            throw new RuntimeException("尚未定义该业务场景相关扩展点 [" + businessScenario + "]");
-        }
-
-        return (T) definition.getExtensionPoint();
-    }
-
-    /**
-     * 注册扩展点
-     * @param point
-     */
-    private void doRegister(@NotNull ExtensionPoint point) {
-        Class<?>  extensionClazz = point.getClass();
-
-        if (AopUtils.isAopProxy(point)) {
-            extensionClazz = ClassUtils.getUserClass(point);
-        }
-
-        Extension extension = AnnotationUtils.findAnnotation(extensionClazz, Extension.class);
-        final BusinessScenario businessScenario = BusinessScenario.valueOf(extension.businessId(), extension.useCase(), extension.scenario());
-        final ExtensionDefinition definition = ExtensionDefinition.valueOf(businessScenario, point);
-        final ExtensionDefinition exist = registerExtensionBeans.get(businessScenario.getUniqueIdentity());
-        if(exist != null && !exist.equals(definition)) {
-            throw new RuntimeException("相同的业务场景重复注册了不同类型的扩展点实现 :【" + definition + "】【" + exist + "】");
-        }
-
-        registerExtensionBeans.put(businessScenario.getUniqueIdentity(), definition);
-    }
-
-    public void setApplicationContext(ApplicationContext applicationContext) {
-        this.applicationContext = applicationContext;
-    }
-}

+ 0 - 8
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/package-info.java

@@ -1,8 +0,0 @@
-/**
- * @description core 核心
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 21:54
- * @class cn.iocoder.yudao.framework.extension.core.package-info.java
- */
-package cn.iocoder.yudao.framework.extension.core;

+ 0 - 11
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/point/ExtensionPoint.java

@@ -1,11 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core.point;
-/**
- * @description 扩展点 <br/>
- * 表示一块逻辑在不同的业务有不同的实现,使用扩展点做接口申明,然后用{@linkplain cn.iocoder.yudao.framework.extension.core.stereotype.Extension}(扩展)去实现扩展点。
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 22:06
- * @class cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint.java
- */
-public interface ExtensionPoint {
-}

+ 0 - 41
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/stereotype/Extension.java

@@ -1,41 +0,0 @@
-package cn.iocoder.yudao.framework.extension.core.stereotype;
-
-import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
-import org.springframework.stereotype.Component;
-
-import java.lang.annotation.*;
-
-/**
- * @description 表示带注释的类是“扩展组件”
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 21:59
- * @class cn.iocoder.yudao.framework.extension.core.stereotype.Extension.java
- */
-@Inherited
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE})
-@Component
-public @interface Extension {
-
-    /**
-     * 业务 <br/>
-     * 一个自负盈亏的财务主体,比如tmall、淘宝和零售通就是三个不同的业务
-     * @return
-     */
-    String businessId() default BusinessScenario.DEFAULT_BUSINESS_ID;
-
-    /**
-     * 用例 <br/>
-     * 描述了用户和系统之间的互动,每个用例提供了一个或多个场景。比如,支付订单就是一个典型的用例。
-     * @return
-     */
-    String useCase() default BusinessScenario.DEFAULT_USECASE;
-
-    /**
-     * 场景 <br/>
-     * 场景也被称为用例的实例(Instance),包括用例所有的可能情况(正常的和异常的)。比如对于"订单支付"这个用例,就有“支付宝支付”、“银行卡支付”、"微信支付"等多个场景
-     * @return
-     */
-    String scenario() default BusinessScenario.DEFAULT_SCENARIO;
-}

+ 0 - 8
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/package-info.java

@@ -1,8 +0,0 @@
-/**
- * @description 扩展点组件
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-28 14:35
- * @class cn.iocoder.yudao.framework.extension.package-info.java
- */
-package cn.iocoder.yudao.framework.extension;

+ 0 - 2
yudao-framework/yudao-spring-boot-starter-extension/src/main/resources/META-INF/spring.factories

@@ -1,2 +0,0 @@
-org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-  cn.iocoder.yudao.framework.extension.config.YudaoExtensionAutoConfiguration

+ 0 - 19
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/Application.java

@@ -1,19 +0,0 @@
-package cn.iocoder.yudao.framework.extension;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * @description Application
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-30 10:32
- * @class cn.iocoder.yudao.framework.extension.Application.java
- */
-@SpringBootApplication
-public class Application {
-
-    public static void main(String[] args) {
-        SpringApplication.run(Application.class, args);
-    }
-}

+ 0 - 43
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/ExtensionTest.java

@@ -1,43 +0,0 @@
-package cn.iocoder.yudao.framework.extension;
-
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.json.JSONUtil;
-import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
-import cn.iocoder.yudao.framework.extension.core.context.ExtensionExecutor;
-import cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint;
-import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand;
-import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult;
-import lombok.extern.slf4j.Slf4j;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-import java.math.BigDecimal;
-
-/**
- * @description 
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-30 10:30
- * @class cn.iocoder.yudao.framework.extension.ExtensionTest.java
- */
-@RunWith(SpringJUnit4ClassRunner.class)
-@SpringBootTest(classes = Application.class)
-@Slf4j
-public class ExtensionTest {
-
-    @Autowired
-    private ExtensionExecutor extensionExecutor;
-
-    @Test
-    public void unifiedOrder() {
-        final BusinessScenario scenario = BusinessScenario.valueOf("pay", "jsapi", "wechat");
-        final TransactionsCommand command = new TransactionsCommand(IdUtil.objectId(), new BigDecimal(105), "Image形象店-深圳腾大-QQ公仔", "https://www.weixin.qq.com/wxpay/pay.php");
-        final TransactionsResult result = extensionExecutor.execute(PayExtensionPoint.class, scenario, extension -> extension.unifiedOrder(command));
-        log.info("result is: {}", JSONUtil.toJsonStr(result));
-        Assert.assertSame("wechat", result.getChannel());
-    }
-}

+ 0 - 8
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/package-info.java

@@ -1,8 +0,0 @@
-/**
- * @description
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-30 10:25
- * @class cn.iocoder.yudao.framework.extension.package-info.java
- */
-package cn.iocoder.yudao.framework.extension;

+ 0 - 22
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/PayExtensionPoint.java

@@ -1,22 +0,0 @@
-package cn.iocoder.yudao.framework.extension.pay;
-
-import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
-import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand;
-import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult;
-
-/**
- * @description 支付操作接口
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-30 10:35
- * @class cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint.java
- */
-public interface PayExtensionPoint extends ExtensionPoint {
-
-    /**
-     * 统一下单:获取"预支付交易会话标识"
-     * @param command
-     * @return
-     */
-    TransactionsResult unifiedOrder(TransactionsCommand command);
-}

+ 0 - 40
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/command/TransactionsCommand.java

@@ -1,40 +0,0 @@
-package cn.iocoder.yudao.framework.extension.pay.command;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-
-/**
- * @description 下单请求
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-30 10:48
- * @class cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand.java
- */
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class TransactionsCommand implements Serializable {
-    /**
-     * 订单编号
-     */
-    private String orderNo;
-
-    /**
-     * 支付金额
-     */
-    private BigDecimal amount;
-
-    /**
-     * 商品描述
-     */
-    private String productDescription;
-
-    /**
-     * 通知地址
-     */
-    private String notifyUrl;
-}

+ 0 - 39
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/domain/TransactionsResult.java

@@ -1,39 +0,0 @@
-package cn.iocoder.yudao.framework.extension.pay.domain;
-
-import lombok.*;
-
-import java.io.Serializable;
-
-/**
- * @description 下单: 预支付交易单返回结果
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-30 10:43
- * @class cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult.java
- */
-@Data
-@AllArgsConstructor
-public class TransactionsResult implements Serializable {
-
-    /**
-     * 预支付交易会话标识
-     */
-    private String prepayId;
-
-    /**
-     * 订单编号
-     */
-    private String orderNo;
-
-    /**
-     * 系统内部支付单号
-     */
-    private String paymentNo;
-
-    /**
-     * 支付渠道:微信 or 支付宝
-     */
-    private String channel;
-
-
-}

+ 0 - 26
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/impl/AlipayService.java

@@ -1,26 +0,0 @@
-package cn.iocoder.yudao.framework.extension.pay.impl;
-
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.json.JSONUtil;
-import cn.iocoder.yudao.framework.extension.core.stereotype.Extension;
-import cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint;
-import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand;
-import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * @description 微信 JSAPI 支付
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-30 10:38
- * @class cn.iocoder.yudao.framework.extension.pay.impl.AlipayService.java
- */
-@Extension(businessId = "pay", useCase = "jsapi", scenario = "alipay")
-@Slf4j
-public class AlipayService implements PayExtensionPoint {
-    @Override
-    public TransactionsResult unifiedOrder(TransactionsCommand command) {
-        log.info("微信 JSAPI 支付:{}", JSONUtil.toJsonStr(command));
-        return new TransactionsResult("alipay26112221580621e9b071c00d9e093b0000", command.getOrderNo(), IdUtil.objectId(), "alipay");
-    }
-}

+ 0 - 26
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/impl/WechatPayService.java

@@ -1,26 +0,0 @@
-package cn.iocoder.yudao.framework.extension.pay.impl;
-
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.json.JSONUtil;
-import cn.iocoder.yudao.framework.extension.core.stereotype.Extension;
-import cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint;
-import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand;
-import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * @description 微信 JSAPI 支付
- * @author Qingchen
- * @version 1.0.0
- * @date 2021-08-30 10:37
- * @class cn.iocoder.yudao.framework.extension.pay.impl.WechatPayService.java
- */
-@Extension(businessId = "pay", useCase = "jsapi", scenario = "wechat")
-@Slf4j
-public class WechatPayService implements PayExtensionPoint {
-    @Override
-    public TransactionsResult unifiedOrder(TransactionsCommand command) {
-        log.info("微信 JSAPI 支付:{}", JSONUtil.toJsonStr(command));
-        return new TransactionsResult("wx26112221580621e9b071c00d9e093b0000", command.getOrderNo(), IdUtil.objectId(), "wechat");
-    }
-}

+ 0 - 19
yudao-framework/yudao-spring-boot-starter-extension/《芋道 Spring Boot 扩展点组件》.md

@@ -1,19 +0,0 @@
-### 作用
-
-​		为了解决同一个流程不同业务有不同处理逻辑而产生,减少代码中 if else 逻辑,降低代码的耦合性,通过统一的扩展形式来支撑业务的变化。
-
-### 原理
-
-​		https://blog.csdn.net/significantfrank/article/details/100074716
-
-
-
-### 使用介绍
-
-参考测试代码 `cn.iocoder.yudao.framework.extension.ExtensionTest`
-
- 
-
-
-
-

+ 6 - 0
yudao-framework/yudao-spring-boot-starter-monitor/pom.xml

@@ -58,6 +58,12 @@
             <artifactId>apm-toolkit-opentracing</artifactId>
         </dependency>
 
+        <!-- Micrometer 对 Prometheus 的支持 -->
+        <dependency>
+            <groupId>io.micrometer</groupId>
+            <artifactId>micrometer-registry-prometheus</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>de.codecentric</groupId>
             <artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->

+ 27 - 0
yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoMetricsAutoConfiguration.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.framework.tracer.config;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Metrics 配置类
+ *
+ * @author 芋道源码
+ */
+@Configuration
+@ConditionalOnClass({MeterRegistryCustomizer.class})
+@ConditionalOnProperty(prefix = "yudao.metrics", value = "enable", matchIfMissing = true) // 允许使用 yudao.metrics.enable=false 禁用 Metrics
+public class YudaoMetricsAutoConfiguration {
+
+    @Bean
+    public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags(
+            @Value("${spring.application.name}") String applicationName) {
+        return registry -> registry.config().commonTags("application", applicationName);
+    }
+
+}

+ 2 - 1
yudao-framework/yudao-spring-boot-starter-monitor/src/main/resources/META-INF/spring.factories

@@ -1,2 +1,3 @@
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-  cn.iocoder.yudao.framework.tracer.config.YudaoTracerAutoConfiguration
+  cn.iocoder.yudao.framework.tracer.config.YudaoTracerAutoConfiguration,\
+  cn.iocoder.yudao.framework.tracer.config.YudaoMetricsAutoConfiguration

+ 3 - 3
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/SecurityProperties.java

@@ -16,18 +16,18 @@ public class SecurityProperties {
      * HTTP 请求时,访问令牌的请求 Header
      */
     @NotEmpty(message = "Token Header 不能为空")
-    private String tokenHeader;
+    private String tokenHeader = "Authorization";
 
     /**
      * mock 模式的开关
      */
     @NotNull(message = "mock 模式的开关不能为空")
-    private Boolean mockEnable;
+    private Boolean mockEnable = false;
     /**
      * mock 模式的密钥
      * 一定要配置密钥,保证安全性
      */
     @NotEmpty(message = "mock 模式的密钥不能为空") // 这里设置了一个默认值,因为实际上只有 mockEnable 为 true 时才需要配置。
-    private String mockSecret = "yudaoyuanma";
+    private String mockSecret = "test";
 
 }

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java

@@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPoint
 import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
 import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkServiceImpl;
 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
-import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
+import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
 import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
 import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -81,7 +81,7 @@ public class YudaoSecurityAutoConfiguration {
         return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler, oauth2TokenApi);
     }
 
-    @Bean("ss") // 使用 Spring Security 的缩写,方便
+    @Bean("ss") // 使用 Spring Security 的缩写,方便使
     public SecurityFrameworkService securityFrameworkService(PermissionApi permissionApi) {
         return new SecurityFrameworkServiceImpl(permissionApi);
     }

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java

@@ -10,8 +10,8 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
-import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
-import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
+import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
+import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
 import lombok.RequiredArgsConstructor;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.web.filter.OncePerRequestFilter;

+ 7 - 0
yudao-framework/yudao-spring-boot-starter-web/pom.xml

@@ -53,6 +53,13 @@
             <scope>provided</scope> <!-- 设置为 provided,主要是 GlobalExceptionHandler 使用 -->
         </dependency>
 
+        <!-- 业务组件 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-infra-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
+            <version>${revision}</version>
+        </dependency>
+
         <!-- 服务保障相关 -->
         <dependency>
             <groupId>io.github.resilience4j</groupId>

+ 18 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogAutoConfiguration.java

@@ -2,11 +2,17 @@ package cn.iocoder.yudao.framework.apilog.config;
 
 import cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter;
 import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;
+import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkServiceImpl;
+import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
+import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkServiceImpl;
+import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
 import cn.iocoder.yudao.framework.web.config.WebProperties;
 import cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration;
-import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
+import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
+import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -17,10 +23,21 @@ import javax.servlet.Filter;
 @AutoConfigureAfter(YudaoWebAutoConfiguration.class)
 public class YudaoApiLogAutoConfiguration {
 
+    @Bean
+    public ApiAccessLogFrameworkService apiAccessLogFrameworkService(ApiAccessLogApi apiAccessLogApi) {
+        return new ApiAccessLogFrameworkServiceImpl(apiAccessLogApi);
+    }
+
+    @Bean
+    public ApiErrorLogFrameworkService apiErrorLogFrameworkService(ApiErrorLogApi apiErrorLogApi) {
+        return new ApiErrorLogFrameworkServiceImpl(apiErrorLogApi);
+    }
+
     /**
      * 创建 ApiAccessLogFilter Bean,记录 API 请求日志
      */
     @Bean
+    @ConditionalOnProperty(prefix = "yudao.access-log", value = "enable", matchIfMissing = true) // 允许使用 yudao.access-log.enable=false 禁用访问日志
     public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties,
                                                                          @Value("${spring.application.name}") String applicationName,
                                                                          ApiAccessLogFrameworkService apiAccessLogFrameworkService) {

+ 4 - 4
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java

@@ -3,8 +3,8 @@ package cn.iocoder.yudao.framework.apilog.core.filter;
 import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.extra.servlet.ServletUtil;
+import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLog;
 import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;
-import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiAccessLogCreateReqDTO;
 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
@@ -66,16 +66,16 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
 
     private void createApiAccessLog(HttpServletRequest request, Date beginTime,
                                     Map<String, String> queryString, String requestBody, Exception ex) {
-        ApiAccessLogCreateReqDTO accessLog = new ApiAccessLogCreateReqDTO();
+        ApiAccessLog accessLog = new ApiAccessLog();
         try {
             this.buildApiAccessLogDTO(accessLog, request, beginTime, queryString, requestBody, ex);
-            apiAccessLogFrameworkService.createApiAccessLogAsync(accessLog);
+            apiAccessLogFrameworkService.createApiAccessLog(accessLog);
         } catch (Throwable th) {
             log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th);
         }
     }
 
-    private void buildApiAccessLogDTO(ApiAccessLogCreateReqDTO accessLog, HttpServletRequest request, Date beginTime,
+    private void buildApiAccessLogDTO(ApiAccessLog accessLog, HttpServletRequest request, Date beginTime,
                                       Map<String, String> queryString, String requestBody, Exception ex) {
         // 处理用户信息
         accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request));

+ 85 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java

@@ -0,0 +1,85 @@
+package cn.iocoder.yudao.framework.apilog.core.service;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+
+/**
+ * API 访问日志
+ *
+ * @author 芋道源码
+ */
+@Data
+public class ApiAccessLog {
+
+    /**
+     * 链路追踪编号
+     */
+    private String traceId;
+    /**
+     * 用户编号
+     */
+    private Long userId;
+    /**
+     * 用户类型
+     */
+    private Integer userType;
+    /**
+     * 应用名
+     */
+    @NotNull(message = "应用名不能为空")
+    private String applicationName;
+
+    /**
+     * 请求方法名
+     */
+    @NotNull(message = "http 请求方法不能为空")
+    private String requestMethod;
+    /**
+     * 访问地址
+     */
+    @NotNull(message = "访问地址不能为空")
+    private String requestUrl;
+    /**
+     * 请求参数
+     */
+    @NotNull(message = "请求参数不能为空")
+    private String requestParams;
+    /**
+     * 用户 IP
+     */
+    @NotNull(message = "ip 不能为空")
+    private String userIp;
+    /**
+     * 浏览器 UA
+     */
+    @NotNull(message = "User-Agent 不能为空")
+    private String userAgent;
+
+    /**
+     * 开始请求时间
+     */
+    @NotNull(message = "开始请求时间不能为空")
+    private Date beginTime;
+    /**
+     * 结束请求时间
+     */
+    @NotNull(message = "结束请求时间不能为空")
+    private Date endTime;
+    /**
+     * 执行时长,单位:毫秒
+     */
+    @NotNull(message = "执行时长不能为空")
+    private Integer duration;
+    /**
+     * 结果码
+     */
+    @NotNull(message = "错误码不能为空")
+    private Integer resultCode;
+    /**
+     * 结果提示
+     */
+    private String resultMsg;
+
+}

+ 2 - 6
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkService.java

@@ -1,9 +1,5 @@
 package cn.iocoder.yudao.framework.apilog.core.service;
 
-import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiAccessLogCreateReqDTO;
-
-import javax.validation.Valid;
-
 /**
  * API 访问日志 Framework Service 接口
  *
@@ -14,8 +10,8 @@ public interface ApiAccessLogFrameworkService {
     /**
      * 创建 API 访问日志
      *
-     * @param createDTO 创建信息
+     * @param apiAccessLog API 访问日志
      */
-    void createApiAccessLogAsync(@Valid ApiAccessLogCreateReqDTO createDTO);
+    void createApiAccessLog(ApiAccessLog apiAccessLog);
 
 }

+ 28 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.framework.apilog.core.service;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
+import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.scheduling.annotation.Async;
+
+/**
+ * API 访问日志 Framework Service 实现类
+ *
+ * 基于 {@link ApiAccessLogApi} 服务,记录访问日志
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkService {
+
+    private final ApiAccessLogApi apiAccessLogApi;
+
+    @Override
+    @Async
+    public void createApiAccessLog(ApiAccessLog apiAccessLog) {
+        ApiAccessLogCreateReqDTO reqDTO = BeanUtil.copyProperties(apiAccessLog, ApiAccessLogCreateReqDTO.class);
+        apiAccessLogApi.createApiAccessLog(reqDTO);
+    }
+
+}

+ 107 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java

@@ -0,0 +1,107 @@
+package cn.iocoder.yudao.framework.apilog.core.service;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+
+/**
+ * API 错误日志
+ *
+ * @author 芋道源码
+ */
+@Data
+public class ApiErrorLog {
+
+    /**
+     * 链路编号
+     */
+    private String traceId;
+    /**
+     * 账号编号
+     */
+    private Long userId;
+    /**
+     * 用户类型
+     */
+    private Integer userType;
+    /**
+     * 应用名
+     */
+    @NotNull(message = "应用名不能为空")
+    private String applicationName;
+
+    /**
+     * 请求方法名
+     */
+    @NotNull(message = "http 请求方法不能为空")
+    private String requestMethod;
+    /**
+     * 访问地址
+     */
+    @NotNull(message = "访问地址不能为空")
+    private String requestUrl;
+    /**
+     * 请求参数
+     */
+    @NotNull(message = "请求参数不能为空")
+    private String requestParams;
+    /**
+     * 用户 IP
+     */
+    @NotNull(message = "ip 不能为空")
+    private String userIp;
+    /**
+     * 浏览器 UA
+     */
+    @NotNull(message = "User-Agent 不能为空")
+    private String userAgent;
+
+    /**
+     * 异常时间
+     */
+    @NotNull(message = "异常时间不能为空")
+    private Date exceptionTime;
+    /**
+     * 异常名
+     */
+    @NotNull(message = "异常名不能为空")
+    private String exceptionName;
+    /**
+     * 异常发生的类全名
+     */
+    @NotNull(message = "异常发生的类全名不能为空")
+    private String exceptionClassName;
+    /**
+     * 异常发生的类文件
+     */
+    @NotNull(message = "异常发生的类文件不能为空")
+    private String exceptionFileName;
+    /**
+     * 异常发生的方法名
+     */
+    @NotNull(message = "异常发生的方法名不能为空")
+    private String exceptionMethodName;
+    /**
+     * 异常发生的方法所在行
+     */
+    @NotNull(message = "异常发生的方法所在行不能为空")
+    private Integer exceptionLineNumber;
+    /**
+     * 异常的栈轨迹异常的栈轨迹
+     */
+    @NotNull(message = "异常的栈轨迹不能为空")
+    private String exceptionStackTrace;
+    /**
+     * 异常导致的根消息
+     */
+    @NotNull(message = "异常导致的根消息不能为空")
+    private String exceptionRootCauseMessage;
+    /**
+     * 异常导致的消息
+     */
+    @NotNull(message = "异常导致的消息不能为空")
+    private String exceptionMessage;
+
+
+}

+ 2 - 6
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkService.java

@@ -1,9 +1,5 @@
 package cn.iocoder.yudao.framework.apilog.core.service;
 
-import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiErrorLogCreateReqDTO;
-
-import javax.validation.Valid;
-
 /**
  * API 错误日志 Framework Service 接口
  *
@@ -14,8 +10,8 @@ public interface ApiErrorLogFrameworkService {
     /**
      * 创建 API 错误日志
      *
-     * @param createDTO 创建信息
+     * @param apiErrorLog API 错误日志
      */
-    void createApiErrorLogAsync(@Valid ApiErrorLogCreateReqDTO createDTO);
+    void createApiErrorLog(ApiErrorLog apiErrorLog);
 
 }

+ 28 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.framework.apilog.core.service;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
+import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.scheduling.annotation.Async;
+
+/**
+ * API 错误日志 Framework Service 实现类
+ *
+ * 基于 {@link ApiErrorLogApi} 服务,记录错误日志
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkService {
+
+    private final ApiErrorLogApi apiErrorLogApi;
+
+    @Override
+    @Async
+    public void createApiErrorLog(ApiErrorLog apiErrorLog) {
+        ApiErrorLogCreateReqDTO reqDTO = BeanUtil.copyProperties(apiErrorLog, ApiErrorLogCreateReqDTO.class);
+        apiErrorLogApi.createApiErrorLog(reqDTO);
+    }
+
+}

+ 15 - 12
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java

@@ -1,8 +1,8 @@
 package cn.iocoder.yudao.framework.swagger.config;
 
+import cn.iocoder.yudao.framework.swagger.core.SpringFoxHandlerProviderBeanPostProcessor;
 import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
@@ -21,6 +21,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
 import java.util.Collections;
 import java.util.List;
 
+import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
 import static springfox.documentation.builders.RequestHandlerSelectors.basePackage;
 
 /**
@@ -38,27 +39,27 @@ import static springfox.documentation.builders.RequestHandlerSelectors.basePacka
 public class YudaoSwaggerAutoConfiguration {
 
     @Bean
-    @ConditionalOnMissingBean
-    public SwaggerProperties swaggerProperties() {
-        return new SwaggerProperties();
+    public SpringFoxHandlerProviderBeanPostProcessor springFoxHandlerProviderBeanPostProcessor() {
+        return new SpringFoxHandlerProviderBeanPostProcessor();
     }
 
     @Bean
-    public Docket createRestApi() {
-        SwaggerProperties properties = swaggerProperties();
+    public Docket createRestApi(SwaggerProperties properties) {
         // 创建 Docket 对象
         return new Docket(DocumentationType.SWAGGER_2)
-                // 用来创建该 API 的基本信息,展示在文档的页面中(自定义展示的信息)
+                // 用来创建该 API 的基本信息,展示在文档的页面中(自定义展示的信息)
                 .apiInfo(apiInfo(properties))
-                // 设置扫描指定 package 包下的
+                // 设置扫描指定 package 包下的
                 .select()
                 .apis(basePackage(properties.getBasePackage()))
-//                .apis(basePackage("cn.iocoder.yudao.module.infra")) // 可用于 swagger 无法展示时使用
+//                .apis(basePackage("cn.iocoder.yudao.module.system")) // 可用于 swagger 无法展示时使用
                 .paths(PathSelectors.any())
                 .build()
+                // ③ 安全上下文(认证)
                 .securitySchemes(securitySchemes())
-                .globalRequestParameters(globalRequestParameters())
-                .securityContexts(securityContexts());
+                .securityContexts(securityContexts())
+                // ④ 全局参数(多租户 header)
+                .globalRequestParameters(globalRequestParameters());
     }
 
     // ========== apiInfo ==========
@@ -93,6 +94,7 @@ public class YudaoSwaggerAutoConfiguration {
     private static List<SecurityContext> securityContexts() {
         return Collections.singletonList(SecurityContext.builder()
                 .securityReferences(securityReferences())
+                // 通过 PathSelectors.regex("^(?!auth).*$"),排除包含 "auth" 的接口不需要使用securitySchemes
                 .forPaths(PathSelectors.regex("^(?!auth).*$"))
                 .build());
     }
@@ -108,7 +110,8 @@ public class YudaoSwaggerAutoConfiguration {
     // ========== globalRequestParameters ==========
 
     private static List<RequestParameter> globalRequestParameters() {
-        RequestParameterBuilder tenantParameter = new RequestParameterBuilder().name("tenant-id").description("租户编号")
+        RequestParameterBuilder tenantParameter = new RequestParameterBuilder()
+                .name(HEADER_TENANT_ID).description("租户编号")
                 .in(ParameterType.HEADER).example(new ExampleBuilder().value(1L).build());
         return Collections.singletonList(tenantParameter.build());
     }

+ 43 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/SpringFoxHandlerProviderBeanPostProcessor.java

@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.framework.swagger.core;
+
+import cn.hutool.core.util.ReflectUtil;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
+import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
+import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
+
+import java.util.List;
+
+/**
+ * 解决 SpringFox 与 SpringBoot 2.6.x 不兼容的问题
+ * 该问题对应的 issue 为 https://github.com/springfox/springfox/issues/3462
+ *
+ * @author 芋道源码
+ */
+public class SpringFoxHandlerProviderBeanPostProcessor implements BeanPostProcessor {
+
+    @Override
+    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+        if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
+            customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
+        }
+        return bean;
+    }
+
+    private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
+        // 移除,只保留 patternParser
+        List<T> copy = CollectionUtils.filterList(mappings, mapping -> mapping.getPatternParser() == null);
+        // 添加到 mappings 中
+        mappings.clear();
+        mappings.addAll(copy);
+    }
+
+    @SuppressWarnings("unchecked")
+    private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
+        return (List<RequestMappingInfoHandlerMapping>)
+                ReflectUtil.getFieldValue(bean, "handlerMappings");
+    }
+
+}

+ 6 - 2
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/WebProperties.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.framework.web.config;
 
+import lombok.AllArgsConstructor;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
@@ -15,14 +17,16 @@ import javax.validation.constraints.NotNull;
 public class WebProperties {
 
     @NotNull(message = "APP API 不能为空")
-    private Api appApi;
+    private Api appApi = new Api("/app-api", "**.controller.app.**");
     @NotNull(message = "Admin API 不能为空")
-    private Api adminApi;
+    private Api adminApi = new Api("/admin-api", "**.controller.admin.**");
 
     @NotNull(message = "Admin UI 不能为空")
     private Ui adminUi;
 
     @Data
+    @AllArgsConstructor
+    @NoArgsConstructor
     @Valid
     public static class Api {
 

+ 0 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java


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