Browse Source

Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master_unit-sql

# Conflicts:
#	yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql
YunaiV 2 years ago
parent
commit
f770133854
100 changed files with 2715 additions and 918 deletions
  1. 98 55
      README.md
  2. 1 1
      http-client.env.json
  3. 14 6
      pom.xml
  4. 1 0
      sql/mysql/optional/jimureport.mysql5.7.create.sql
  5. 118 0
      sql/mysql/optional/mp.sql
  6. 1 0
      sql/mysql/optional/vue3-menu.sql
  7. 498 133
      sql/mysql/ruoyi-vue-pro.sql
  8. 0 85
      sql/optional/mall/coupon.sql
  9. 0 315
      sql/optional/mall/mall.sql
  10. 0 78
      sql/optional/mall/order.sql
  11. 47 22
      yudao-dependencies/pom.xml
  12. 2 2
      yudao-example/yudao-sso-demo-by-code/pom.xml
  13. 2 2
      yudao-example/yudao-sso-demo-by-password/pom.xml
  14. 2 0
      yudao-framework/pom.xml
  15. 5 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
  16. 0 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java
  17. 4 0
      yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java
  18. 43 0
      yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java
  19. 15 0
      yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtilsTest.java
  20. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java
  21. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml
  22. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkServiceImpl.java
  23. 26 0
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java
  24. 1 0
      yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml
  25. 4 5
      yudao-framework/yudao-spring-boot-starter-captcha/pom.xml
  26. 1 1
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java
  27. 1 4
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java
  28. 1 2
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/service/RedisCaptchaServiceImpl.java
  29. 0 0
      yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/META-INF/services/com.xingyuv.captcha.service.CaptchaCacheService
  30. 38 0
      yudao-framework/yudao-spring-boot-starter-desensitize/pom.xml
  31. 32 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java
  32. 21 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/handler/DesensitizationHandler.java
  33. 92 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java
  34. 4 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/package-info.java
  35. 36 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/EmailDesensitize.java
  36. 38 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/RegexDesensitize.java
  37. 38 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java
  38. 21 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java
  39. 22 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java
  40. 40 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/BankCardDesensitize.java
  41. 40 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java
  42. 40 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java
  43. 40 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java
  44. 40 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/IdCardDesensitize.java
  45. 40 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/MobileDesensitize.java
  46. 42 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/PasswordDesensitize.java
  47. 43 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/SliderDesensitize.java
  48. 78 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java
  49. 27 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/BankCardDesensitization.java
  50. 25 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java
  51. 27 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java
  52. 25 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java
  53. 25 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java
  54. 25 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/IdCardDesensitization.java
  55. 26 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/MobileDesensitization.java
  56. 25 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/PasswordDesensitization.java
  57. 98 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/DesensitizeTest.java
  58. 30 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/annotation/Address.java
  59. 19 0
      yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/handler/AddressHandler.java
  60. 7 1
      yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java
  61. 1 0
      yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java
  62. 17 6
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
  63. 2 0
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java
  64. 4 4
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java
  65. 2 0
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
  66. 8 1
      yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java
  67. 13 1
      yudao-framework/yudao-spring-boot-starter-web/pom.xml
  68. 3 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/package-info.java
  69. 18 11
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
  70. 1 2
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/SpringFoxHandlerProviderBeanPostProcessor.java
  71. 3 13
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java
  72. 0 136
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/XssRequestWrapper.java
  73. 1 1
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/XssProperties.java
  74. 60 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java
  75. 64 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/clean/JsoupXssCleaner.java
  76. 16 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/clean/XssCleaner.java
  77. 6 5
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/filter/XssFilter.java
  78. 92 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/filter/XssRequestWrapper.java
  79. 59 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/json/XssStringJsonDeserializer.java
  80. 6 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/package-info.java
  81. 2 1
      yudao-framework/yudao-spring-boot-starter-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  82. 37 0
      yudao-framework/yudao-spring-boot-starter-websocket/pom.xml
  83. 14 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketHandlerConfig.java
  84. 29 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketProperties.java
  85. 34 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/YudaoWebSocketAutoConfiguration.java
  86. 24 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/UserHandshakeInterceptor.java
  87. 9 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketKeyDefine.java
  88. 24 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketMessageDO.java
  89. 36 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketSessionHandler.java
  90. 31 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketUtils.java
  91. 49 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/YudaoWebSocketHandlerDecorator.java
  92. 1 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/package-info.java
  93. 1 0
      yudao-framework/yudao-spring-boot-starter-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  94. 7 7
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
  95. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImplTest.java
  96. 6 0
      yudao-module-infra/yudao-module-infra-biz/pom.xml
  97. 6 6
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java
  98. 8 1
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java
  99. 22 5
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenColumnDO.java
  100. 5 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java

+ 98 - 55
README.md

@@ -25,7 +25,7 @@
 
 * 管理后台的 Vue3 版本采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) ,Vue2 版本采用 [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) 
 * 管理后台的移动端采用 [uni-app](https://github.com/dcloudio/uni-app) 方案,一份代码多终端适配,同时支持 APP、小程序、H5!
-* 后端采用 Spring Boot、MySQL + MyBatis Plus、Redis + Redisson
+* 后端采用 Spring Boot 多模块架构、MySQL + MyBatis Plus、Redis + Redisson
 * 数据库可使用 MySQL、Oracle、PostgreSQL、SQL Server、MariaDB、国产达梦 DM、TiDB 等
 * 权限认证使用 Spring Security & Token & Redis,支持多终端、多种用户的认证系统,支持 SSO 单点登录
 * 支持加载动态权限菜单,按钮级别权限控制,本地缓存提升性能
@@ -36,12 +36,25 @@
 * 集成阿里云、腾讯云等短信渠道,集成 MinIO、阿里云、腾讯云、七牛云等云存储服务
 * 集成报表设计器,支持数据报表、图形报表、打印设计等
 
-| 项目名                | 说明                     | 传送门                                                                                                                               |
-|--------------------|------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
-| `ruoyi-vue-pro`    | Spring Boot 多模块        | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-pro)**     [Github](https://github.com/YunaiV/ruoyi-vue-pro)   |
-| `yudao-cloud`  | Spring Cloud 微服务       | **[Gitee](https://gitee.com/zhijiantianya/yudao-cloud)**     [Github](https://github.com/YunaiV/yudao-cloud)       |
-| `Spring-Boot-Labs` | Spring Boot & Cloud 入门 | **[Gitee](https://gitee.com/zhijiantianya/SpringBoot-Labs)**     [Github](https://github.com/YunaiV/SpringBoot-Labs) |
- | `ruoyi-vue-pro-mini` | 精简版 移除工作流 支付等模块| **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/mini)**  |
+| 项目名                  | 说明                     | 传送门                                                                                                                                 |
+|----------------------|------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
+| `ruoyi-vue-pro`      | Spring Boot 多模块        | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-pro)**     [Github](https://github.com/YunaiV/ruoyi-vue-pro)     |
+| `yudao-cloud`        | Spring Cloud 微服务       | **[Gitee](https://gitee.com/zhijiantianya/yudao-cloud)**     [Github](https://github.com/YunaiV/yudao-cloud)         |
+| `Spring-Boot-Labs`   | Spring Boot & Cloud 入门 | **[Gitee](https://gitee.com/zhijiantianya/SpringBoot-Labs)**     [Github](https://github.com/YunaiV/SpringBoot-Labs) |
+
+## 🐰 分支说明
+
+|       | JDK 8 完整版                                                 | JDK 8 精简版                                                          | JDK 17 完整版                                                                  |
+|-------|-----------------------------------------------------------|--------------------------------------------------------------------|-----------------------------------------------------------------------------|
+| 分支    | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`mini`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/mini/) | [`boot-dev`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/boot3-dev/) |
+| 说明    | 包括所有功能                                                    | 只保留核心功能                                                            | 适配 Spring Boot 3.X                                                          |
+| 系统功能  | √                                                         | √                                                                  | √                                                                           |
+| 基础设施  | √                                                         | √                                                                  | √                                                                           |
+| 会员中心  | √                                                         | √                                                                  | √                                                                           |
+| 工作流程  | √                                                         | x                                                                  | 适配中                                                                         |
+| 数据报表  | √                                                         | x                                                                  | 适配中                                                                         |
+| 商城系统  | √                                                         | x                                                                  | √                                                                           |
+| 微信公众号 | √                                                         | x                                                                  | √                                                                           |
 
 ## 😎 开源协议
 
@@ -55,6 +68,14 @@
 
 ③ 代码整洁、架构整洁,遵循《阿里巴巴 Java 开发手册》规范,代码注释详细,57000 行 Java 代码,22000 行代码注释。
 
+## 🤝 项目外包
+
+我们也是接外包滴,如果你有项目想要外包,可以微信联系【**Aix9975**】。
+
+团队包含专业的项目经理、架构师、前端工程师、后端工程师、测试工程师、运维工程师,可以提供全流程的外包服务。
+
+项目可以是商城、SCRM 系统、OA 系统、物流系统、ERP 系统、CMS 系统、HIS 系统、支付系统、IM 聊天、微信公众号、微信小程序等等。
+
 ## 🐼 内置功能
 
 系统内置多种多种业务功能,可以用于快速你的业务系统:
@@ -68,6 +89,7 @@
 * 会员中心
 * 数据报表
 * 商城系统
+* 微信公众号
 
 > 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
 >
@@ -90,12 +112,15 @@
 | 🚀  | 租户套餐  | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限       |
 |     | 字典管理  | 对系统中经常使用的一些较为固定的数据进行维护          |
 | 🚀  | 短信管理  | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
+| 🚀  | 邮件管理  | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台       |
+| 🚀  | 站内信   | 系统内的消息通知,提供站内信模版、站内信消息          |
 | 🚀  | 操作日志  | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
 | ⭐️  | 登录日志  | 系统登录日志记录查询,包含登录异常               |
 | 🚀  | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务     |
 |     | 通知公告  | 系统通知公告信息发布维护                    |
-| 🚀  | 敏感词  | 配置系统敏感词,支持标签分组                  |
+| 🚀  | 敏感词   | 配置系统敏感词,支持标签分组                  |
 | 🚀  | 应用管理  | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
+| 🚀  | 地区管理  | 展示省份、城市、区镇等城市信息,支持 IP 对应城市      |
 
 ### 工作流程
 
@@ -130,7 +155,7 @@ ps:核心功能已经实现,正在对接微信小程序中...
 |     | 表单构建     | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件         |
 | 🚀  | 配置管理     | 对系统动态配置常用参数,支持 SpringBoot 加载                 |
 | ⭐️  | 定时任务     | 在线(添加、修改、删除)任务调度包含执行结果日志                     |
-| 🚀  | 文件服务     | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等      | 
+| 🚀  | 文件服务     | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等   | 
 | 🚀  | API 日志   | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题   |
 |     | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈              |
 |     | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理           |
@@ -151,6 +176,21 @@ ps:核心功能已经实现,正在对接微信小程序中...
 | 🚀  | 报表设计器     | 支持数据报表、图形报表、打印设计等       |
 | 🚀  | 大屏设计器     | 建设中... 拖拽式实现可视化数据大屏          |
 
+### 微信公众号
+
+|     | 功能     | 描述                            |
+|-----|--------|-------------------------------|
+| 🚀  | 账号管理   | 配置接入的微信公众号,可支持多个公众号           |
+| 🚀  | 数据统计   | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据  |
+| 🚀  | 粉丝管理   | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 |
+| 🚀  | 消息管理   | 查看粉丝发送的消息列表,可主动回复粉丝消息         |
+| 🚀  | 自动回复   | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 |
+| 🚀  | 标签管理   | 对公众号的标签进行创建、查询、修改、删除等操作       |
+| 🚀  | 菜单管理   | 自定义公众号的菜单,也可以从公众号同步菜单         |
+| 🚀  | 素材管理   | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 |
+| 🚀  | 图文草稿箱  | 新增常用的图文素材到草稿箱,可发布到公众号         |
+| 🚀  | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作           |
+
 ### 商城系统
 
 建设中...
@@ -167,46 +207,49 @@ ps:核心功能已经实现,正在对接微信小程序中...
 
 ## 🐨 技术栈
 
-| 项目                      | 说明                 |
-|-------------------------|-----------------------|
-| `yudao-dependencies`    | Maven 依赖版本管理       |
-| `yudao-framework`       | Java 框架拓展          |
-| `yudao-server`          | 管理后台 + 用户 APP 的服务端 |
-| `yudao-ui-admin`        | 管理后台的 Vue2 前端项目     |
-| `yudao-ui-admin-vue3`   | 管理后台的 Vue3 前端项目     |
-| `yudao-ui-admin-uniapp` | 管理后台的 uni-app 多端项目  |
-| `yudao-ui-app`          | 用户 APP 的 UI 界面     |
-| `yudao-module-system`   | 系统功能的 Module 模块    |
-| `yudao-module-member`   | 会员中心的 Module 模块    |
-| `yudao-module-infra`    | 基础设施的 Module 模块    |
-| `yudao-module-bpm`      | 工作流程的 Module 模块    |
-| `yudao-module-pay`      | 支付系统的 Module 模块    |
+| 项目                           | 说明                 |
+|------------------------------|--------------------|
+| `yudao-dependencies`         | Maven 依赖版本管理       |
+| `yudao-framework`            | Java 框架拓展          |
+| `yudao-server`               | 管理后台 + 用户 APP 的服务端 |
+| `yudao-ui-admin`             | 管理后台的 Vue2 前端项目    |
+| `yudao-ui-admin-vue3`        | 管理后台的 Vue3 前端项目    |
+| `yudao-ui-admin-uniapp`      | 管理后台的 uni-app 多端项目 |
+| `yudao-ui-app`               | 用户 APP 的 UI 界面     |
+| `yudao-module-system`        | 系统功能的 Module 模块    |
+| `yudao-module-member`        | 会员中心的 Module 模块    |
+| `yudao-module-infra`         | 基础设施的 Module 模块    |
+| `yudao-module-bpm`           | 工作流程的 Module 模块    |
+| `yudao-module-pay`           | 支付系统的 Module 模块    |
+| `yudao-module-mall`          | 商城系统的 Module 模块    |
+| `yudao-module-mp`            | 微信公众号的 Module 模块   |
+| `yudao-module-visualization` | 大屏报表 Module 模块     |
 
 ### 后端
 
-| 框架                                                                                         | 说明                   | 版本          | 学习指南                                                           |
-|---------------------------------------------------------------------------------------------|-----------------------|-------------|----------------------------------------------------------------|
-| [Spring Boot](https://spring.io/projects/spring-boot)                                       | 应用开发框架             | 2.7.6       | [文档](https://github.com/YunaiV/SpringBoot-Labs)                |
-| [MySQL](https://www.mysql.com/cn/)                                                          | 数据库服务器             | 5.7 / 8.0+  |                                                                |
-| [Druid](https://github.com/alibaba/druid)                                                   | JDBC 连接池、监控组件     | 1.2.15      | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?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.6.0       | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
-| [Redis](https://redis.io/)                                                                  | key-value 数据库        | 5.0 / 6.0   |                                                                |
-| [Redisson](https://github.com/redisson/redisson)                                            | Redis 客户端            | 3.18.0      | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao)           |
-| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架               | 5.3.24      | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao)               |
-| [Spring Security](https://github.com/spring-projects/spring-security)                       | Spring 安全框架         | 5.7.5       | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
-| [Hibernate Validator](https://github.com/hibernate/hibernate-validator)                     | 参数校验组件             | 6.2.5       | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao)      |
-| [Flowable](https://github.com/flowable/flowable-engine)                                     | 工作流引擎               | 6.7.2       | [文档](https://doc.iocoder.cn/bpm/)                                                     |
-| [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.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)    |
+| 框架                                                                                          | 说明               | 版本          | 学习指南                                                           |
+|---------------------------------------------------------------------------------------------|------------------|-------------|----------------------------------------------------------------|
+| [Spring Boot](https://spring.io/projects/spring-boot)                                       | 应用开发框架           | 2.7.7       | [文档](https://github.com/YunaiV/SpringBoot-Labs)                |
+| [MySQL](https://www.mysql.com/cn/)                                                          | 数据库服务器           | 5.7 / 8.0+  |                                                                |
+| [Druid](https://github.com/alibaba/druid)                                                   | JDBC 连接池、监控组件    | 1.2.15      | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
+| [MyBatis Plus](https://mp.baomidou.com/)                                                    | MyBatis 增强工具包    | 3.5.3.1     | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao)         |
+| [Dynamic Datasource](https://dynamic-datasource.com/)                                       | 动态数据源            | 3.6.1       | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
+| [Redis](https://redis.io/)                                                                  | key-value 数据库    | 5.0 / 6.0   |                                                                |
+| [Redisson](https://github.com/redisson/redisson)                                            | Redis 客户端        | 3.18.0      | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao)           |
+| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架           | 5.3.24      | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao)               |
+| [Spring Security](https://github.com/spring-projects/spring-security)                       | Spring 安全框架      | 5.7.6       | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
+| [Hibernate Validator](https://github.com/hibernate/hibernate-validator)                     | 参数校验组件           | 6.2.5       | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao)      |
+| [Flowable](https://github.com/flowable/flowable-engine)                                     | 工作流引擎            | 6.8.0       | [文档](https://doc.iocoder.cn/bpm/)                              |
+| [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.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.12.0      | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao)      |
-| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin)                       | Spring Boot 监控平台    | 2.7.9       | [文档](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.5.3.Final | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao)       |
-| [Lombok](https://projectlombok.org/)                                                        | 消除冗长的 Java 代码     | 1.18.24     | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao)          |
-| [JUnit](https://junit.org/junit5/)                                                          | Java 单元测试框架        | 5.8.2       | -                                                              |
-| [Mockito](https://github.com/mockito/mockito)                                               | Java Mock 框架         | 4.8.0       | -                                                              |
+| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin)                       | Spring Boot 监控平台 | 2.7.10      | [文档](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.5.3.Final | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao)       |
+| [Lombok](https://projectlombok.org/)                                                        | 消除冗长的 Java 代码    | 1.18.24     | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao)          |
+| [JUnit](https://junit.org/junit5/)                                                          | Java 单元测试框架      | 5.8.2       | -                                                              |
+| [Mockito](https://github.com/mockito/mockito)                                               | Java Mock 框架     | 4.8.0       | -                                                              |
 
 ### [管理后台 Vue2 前端](./yudao-ui-admin)
 
@@ -217,22 +260,22 @@ ps:核心功能已经实现,正在对接微信小程序中...
 
 ### [管理后台 Vue3 前端](./yudao-ui-admin-vue3)
 
-| 框架                                                                  |     说明      |   版本   |
+| 框架                                                                   |      说明      |   版本   |
 |----------------------------------------------------------------------|:------------:|:------:|
-| [Vue](https://staging-cn.vuejs.org/)                                 |   Vue 框架    | 3.2.45 |
-| [Vite](https://cn.vitejs.dev//)                                      | 开发与构建工具  | 4.0.3  |
-| [Element Plus](https://element-plus.org/zh-CN/)                      | Element Plus | 2.2.27 |
+| [Vue](https://staging-cn.vuejs.org/)                                 |    Vue 框架    | 3.2.45 |
+| [Vite](https://cn.vitejs.dev//)                                      |   开发与构建工具    | 4.0.4  |
+| [Element Plus](https://element-plus.org/zh-CN/)                      | Element Plus | 2.2.28 |
 | [TypeScript](https://www.typescriptlang.org/docs/)                   |  TypeScript  | 4.9.4  |
 | [pinia](https://pinia.vuejs.org/)                                    |    vuex5     | 2.0.28 |
-| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) |    国际化     | 9.2.2  |
-| [vxe-table](https://vxetable.cn/)                                    |  vue最强表单  | 4.3.7  |
+| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) |     国际化      | 9.2.2  |
+| [vxe-table](https://vxetable.cn/)                                    |   vue最强表单    | 4.3.9  |
 
 ### [管理后台 uni-app 跨端](./yudao-ui-admin-uniapp)
 
-| 框架                                                                  | 说明               | 版本     |
-|----------------------------------------------------------------------|------------------|--------|
-| [uni-app](hhttps://github.com/dcloudio/uni-app)                                 | 跨平台框架           | 2.0.0 |
-| [uni-ui](https://github.com/dcloudio/uni-ui)                                      | 基于 uni-app 的 UI 框架          | 1.4.20  |
+| 框架                                              | 说明                 | 版本     |
+|-------------------------------------------------|--------------------|--------|
+| [uni-app](hhttps://github.com/dcloudio/uni-app) | 跨平台框架              | 2.0.0  |
+| [uni-ui](https://github.com/dcloudio/uni-ui)    | 基于 uni-app 的 UI 框架 | 1.4.20 |
 
 ## 🐷 演示图
 

+ 1 - 1
http-client.env.json

@@ -15,6 +15,6 @@
 
     "appApi": "http://127.0.0.1:8888/app-api",
     "appToken": "test1",
-    "appTenentId": "1"
+    "appTenantId": "1"
   }
 }

+ 14 - 6
pom.xml

@@ -17,9 +17,10 @@
         <module>yudao-module-system</module>
         <module>yudao-module-infra</module>
         <module>yudao-module-pay</module>
-<!--        <module>yudao-module-bpm</module>-->
-        <module>yudao-module-visualization</module>
-<!--        <module>yudao-module-mall</module>-->
+        <module>yudao-module-bpm</module>
+<!--        <module>yudao-module-visualization</module>-->
+<!--        <module>yudao-module-mp</module>-->
+        <!--        <module>yudao-module-mall</module>-->
         <!-- 示例项目 -->
         <module>yudao-example</module>
     </modules>
@@ -29,15 +30,16 @@
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <properties>
-        <revision>1.6.5-snapshot</revision>
+        <revision>1.7.0-snapshot</revision>
         <!-- Maven 相关 -->
         <java.version>1.8</java.version>
         <maven.compiler.source>${java.version}</maven.compiler.source>
         <maven.compiler.target>${java.version}</maven.compiler.target>
         <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
-        <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
+        <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
         <!-- 看看咋放到 bom 里 -->
         <lombok.version>1.18.24</lombok.version>
+        <spring.boot.version>2.7.8</spring.boot.version>
         <mapstruct.version>1.5.3.Final</mapstruct.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
@@ -64,13 +66,19 @@
                     <artifactId>maven-surefire-plugin</artifactId>
                     <version>${maven-surefire-plugin.version}</version>
                 </plugin>
-                <!-- maven-compiler-plugin 插件,解决 Lombok + MapStruct 组合 -->
+                <!-- maven-compiler-plugin 插件,解决 spring-boot-configuration-processor + Lombok + MapStruct 组合 -->
+                <!-- https://stackoverflow.com/questions/33483697/re-run-spring-boot-configuration-annotation-processor-to-update-generated-metada -->
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-compiler-plugin</artifactId>
                     <version>${maven-compiler-plugin.version}</version>
                     <configuration>
                         <annotationProcessorPaths>
+                            <path>
+                                <groupId>org.springframework.boot</groupId>
+                                <artifactId>spring-boot-configuration-processor</artifactId>
+                                <version>${spring.boot.version}</version>
+                            </path>
                             <path>
                                 <groupId>org.projectlombok</groupId>
                                 <artifactId>lombok</artifactId>

+ 1 - 0
sql/optional/visualization/jimureport.mysql5.7.create.sql → sql/mysql/optional/jimureport.mysql5.7.create.sql

@@ -1344,6 +1344,7 @@ CREATE TABLE `jimu_report_share`  (
   `last_update_time` datetime NULL DEFAULT NULL COMMENT '最后更新时间',
   `term_of_validity` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '有效期(0:永久有效,1:1天,2:7天)',
   `status` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否过期(0未过期,1已过期)',
+  `preview_lock_status` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码锁状态',
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '积木报表预览权限表' ROW_FORMAT = Dynamic;
 

File diff suppressed because it is too large
+ 118 - 0
sql/mysql/optional/mp.sql


+ 1 - 0
sql/mysql/optional/vue3-menu.sql

@@ -262,5 +262,6 @@ INSERT INTO `system_menu` VALUES (1266, '客户端更新', 'system:oauth2-client
 INSERT INTO `system_menu` VALUES (1267, '客户端删除', 'system:oauth2-client:delete', 3, 4, 1263, '', '', '', 0, b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:33', b'0');
 INSERT INTO `system_menu` VALUES (1281, '可视化报表', '', 1, 12, 0, '/visualization', 'ep:histogram', NULL, 0, b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2022-07-10 20:33:30', b'0');
 INSERT INTO `system_menu` VALUES (1282, '积木报表', '', 2, 1, 1281, 'jimu-report', 'ep:histogram', 'visualization/jmreport/index', 0, b'1', b'1', '1', '2022-07-10 20:26:36', '1', '2022-07-28 21:17:34', b'0');
+INSERT INTO `system_menu` VALUES (1283, 'webSocket连接', '', 2, 14, 2, 'webSocket', 'ep:turn-off', 'infra/webSocket/index', 0, b'1', b'1', '1', '2023-01-01 11:43:04', '1', '2023-01-01 11:43:04', b'0');
 
 SET FOREIGN_KEY_CHECKS = 1;

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


+ 0 - 85
sql/optional/mall/coupon.sql

@@ -1,85 +0,0 @@
-DROP TABLE IF EXISTS `coupon`;
-CREATE TABLE `coupon`
-(
-    `id`                        bigint                                                         NOT NULL AUTO_INCREMENT COMMENT '用户ID',
-    `type`                      varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci  NOT NULL COMMENT '优惠券类型 reward-满减 discount-折扣 random-随机',
-    `name`                      varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci   NOT NULL COMMENT '优惠券名称',
-    `coupon_type_id`            bigint UNSIGNED                                                         DEFAULT 0 COMMENT '优惠券类型id',
-    `coupon_code`               varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci  NOT NULL COMMENT '优惠券编码',
-    `member_id`                 bigint UNSIGNED                                                NOT NULL DEFAULT 0 COMMENT '领用人',
-    `use_order_id`              bigint UNSIGNED                                                NOT NULL DEFAULT 0 COMMENT '优惠券使用订单id',
-    `goods_type`                tinyint(1) UNSIGNED                                            NOT NULL DEFAULT 0 COMMENT '适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用',
-    `goods_ids`                 varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '适用商品id',
-    `at_least`                  decimal(10, 2) UNSIGNED                                        NOT NULL DEFAULT 0 COMMENT '最小金额',
-    `money`                     decimal(10, 2) UNSIGNED                                        NOT NULL DEFAULT 0 COMMENT '面额',
-    `discount`                  decimal(10, 2) UNSIGNED                                        NOT NULL DEFAULT 0 COMMENT '1 =< 折扣 <= 9.9 当type为discount时需要添加',
-    `discount_limit`            decimal(10, 2) UNSIGNED                                        NOT NULL DEFAULT 0 COMMENT '最多折扣金额 当type为discount时可选择性添加',
-    `whether_forbid_preference` tinyint(1) UNSIGNED                                            NOT NULL DEFAULT 0 COMMENT '优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用',
-    `whether_expire_notice`     tinyint(1) UNSIGNED                                            NOT NULL DEFAULT 0 COMMENT '是否开启过期提醒0-不开启 1-开启',
-    `expire_notice_fixed_term`  int(11) UNSIGNED                                               NOT NULL DEFAULT 0 COMMENT '过期前N天提醒',
-    `whether_noticed`           tinyint(1) UNSIGNED                                            NOT NULL DEFAULT 0 COMMENT '是否已提醒',
-    `state`                     tinyint(4) UNSIGNED                                            NOT NULL DEFAULT 0 COMMENT '优惠券状态 1已领用(未使用) 2已使用 3已过期',
-    `get_type`                  tinyint(1) UNSIGNED                                            NOT NULL DEFAULT 0 COMMENT '获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取',
-    `fetch_time`                datetime                                                       NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '领取时间',
-    `use_time`                  datetime                                                       NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '使用时间',
-    `start_time`                datetime                                                       NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '可使用的开始时间',
-    `end_time`                  datetime                                                       NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '有效期结束时间',
-    `creator`                   varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci   NULL     DEFAULT '' COMMENT '创建者',
-    `create_time`               datetime                                                       NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    `updater`                   varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci   NULL     DEFAULT '' COMMENT '更新者',
-    `update_time`               datetime                                                       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-    `deleted`                   bit(1)                                                         NOT NULL DEFAULT b'0' COMMENT '是否删除',
-    `tenant_id`                 bigint                                                         NOT NULL DEFAULT 0 COMMENT '租户编号',
-    PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB
-  AUTO_INCREMENT = 119
-  CHARACTER SET = utf8mb4
-  COLLATE = utf8mb4_unicode_ci COMMENT = '优惠券';
-
-DROP TABLE IF EXISTS `coupon_templete`;
-CREATE TABLE `coupon_templete`
-(
-    `id`                        bigint                                                       NOT NULL AUTO_INCREMENT COMMENT '用户ID',
-    `type`                      varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '优惠券类型 reward-满减 discount-折扣 random-随机',
-    `name`                      varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '优惠券名称',
-    `coupon_name_remark`        varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '名称备注',
-    `image`                     varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '优惠券图片',
-    `count`                     int(11)                                                      NOT NULL DEFAULT 0 COMMENT '发放数量',
-    `lead_count`                int(11)                                                      NOT NULL DEFAULT 0 COMMENT '已领取数量',
-    `used_count`                int(11) UNSIGNED                                             NOT NULL DEFAULT 0 COMMENT '已使用数量',
-    `goods_type`                tinyint(1) UNSIGNED                                          NOT NULL DEFAULT 1 COMMENT '适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用',
-    `product_ids`               varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '适用商品id',
-    `has_use_limit`             tinyint(1) UNSIGNED                                          NOT NULL DEFAULT 0 COMMENT '使用门槛0-无门槛 1-有门槛',
-    `at_least`                  decimal(10, 2)                                               NOT NULL DEFAULT 0 COMMENT '满多少元使用 0代表无限制',
-    `money`                     decimal(10, 2)                                               NOT NULL DEFAULT 0 COMMENT '发放面额 当type为reward时需要添加',
-    `discount`                  decimal(10, 2) UNSIGNED                                      NOT NULL DEFAULT 0 COMMENT '1 =< 折扣 <= 9.9 当type为discount时需要添加',
-    `discount_limit`            decimal(10, 2)                                               NOT NULL DEFAULT 0 COMMENT '最多折扣金额 当type为discount时可选择性添加',
-    `min_money`                 decimal(10, 2) UNSIGNED                                      NOT NULL DEFAULT 0 COMMENT '最低金额 当type为radom时需要添加',
-    `max_money`                 decimal(10, 2) UNSIGNED                                      NOT NULL DEFAULT 0 COMMENT '最大金额 当type为radom时需要添加',
-    `validity_type`             tinyint(1) UNSIGNED                                          NOT NULL DEFAULT 0 COMMENT '过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期',
-    `start_use_time`            datetime COMMENT '使用开始日期 过期类型1时必填',
-    `end_use_time`              datetime COMMENT '使用结束日期 过期类型1时必填',
-    `fixed_term`                int(11) UNSIGNED                                             NOT NULL DEFAULT 0 COMMENT '当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效',
-    `whether_limitless`         tinyint(1) UNSIGNED                                          NOT NULL DEFAULT 0 COMMENT '是否无限制0-否 1是',
-    `max_fetch`                 int(11) UNSIGNED                                             NOT NULL DEFAULT 0 COMMENT '每人最大领取个数',
-    `whether_expire_notice`     tinyint(1) UNSIGNED                                          NOT NULL DEFAULT 0 COMMENT '是否开启过期提醒0-不开启 1-开启',
-    `expire_notice_fixed_term`  int(11) UNSIGNED                                             NOT NULL DEFAULT 0 COMMENT '过期前N天提醒',
-    `whether_forbid_preference` tinyint(1) UNSIGNED                                          NOT NULL DEFAULT 0 COMMENT '优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用',
-    `whether_show`              int(11) UNSIGNED                                             NOT NULL DEFAULT 0 COMMENT '是否显示',
-    `discount_order_money`      decimal(10, 2) UNSIGNED                                      NOT NULL DEFAULT 0 COMMENT '订单的优惠总金额',
-    `order_money`               decimal(10, 2) UNSIGNED                                      NOT NULL DEFAULT 0 COMMENT '用券总成交额',
-    `whether_forbidden`         tinyint(1) UNSIGNED                                          NOT NULL DEFAULT 0 COMMENT '是否禁止发放0-否 1-是',
-    `order_goods_num`           int(11) UNSIGNED                                             NOT NULL DEFAULT 0 COMMENT '使用优惠券购买的商品数量',
-    `status`                    tinyint(11)                                                  NOT NULL DEFAULT 0 COMMENT '状态(1进行中2已结束-1已关闭)',
-    `end_time`                  datetime COMMENT '有效日期结束时间',
-    `creator`                   varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL     DEFAULT '' COMMENT '创建者',
-    `create_time`               datetime                                                     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    `updater`                   varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL     DEFAULT '' COMMENT '更新者',
-    `update_time`               datetime                                                     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-    `deleted`                   bit(1)                                                       NOT NULL DEFAULT b'0' COMMENT '是否删除',
-    `tenant_id`                 bigint                                                       NOT NULL DEFAULT 0 COMMENT '租户编号',
-    PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB
-  AUTO_INCREMENT = 119
-  CHARACTER SET = utf8mb4
-  COLLATE = utf8mb4_unicode_ci COMMENT = '优惠券模板';

+ 0 - 315
sql/optional/mall/mall.sql

@@ -1,315 +0,0 @@
-/*
- Navicat Premium Data Transfer
-
- Source Server         : 127.0.0.1 MySQL
- Source Server Type    : MySQL
- Source Server Version : 80026
- Source Host           : localhost:3306
- Source Schema         : ruoyi-vue-pro
-
- Target Server Type    : MySQL
- Target Server Version : 80026
- File Encoding         : 65001
-
- Date: 01/08/2022 23:01:36
-*/
-
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
--- ----------------------------
--- Table structure for market_activity
--- ----------------------------
-DROP TABLE IF EXISTS `market_activity`;
-CREATE TABLE `market_activity`  (
-                                    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '活动编号',
-                                    `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '活动标题',
-                                    `activity_type` tinyint NOT NULL COMMENT '活动类型',
-                                    `status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
-                                    `start_time` datetime NOT NULL COMMENT '开始时间',
-                                    `end_time` datetime NOT NULL COMMENT '结束时间',
-                                    `invalid_time` datetime NULL DEFAULT NULL COMMENT '失效时间',
-                                    `delete_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
-                                    `time_limited_discount` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '限制折扣字符串,使用 JSON 序列化成字符串存储',
-                                    `full_privilege` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '限制折扣字符串,使用 JSON 序列化成字符串存储',
-                                    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-                                    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-                                    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                    PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '促销活动';
-
--- ----------------------------
--- Records of market_activity
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for market_banner
--- ----------------------------
-DROP TABLE IF EXISTS `market_banner`;
-CREATE TABLE `market_banner`  (
-                                  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Banner编号',
-                                  `title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT 'Banner标题',
-                                  `pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '图片URL',
-                                  `status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
-                                  `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '跳转地址',
-                                  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-                                  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-                                  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                  `sort` tinyint NULL DEFAULT NULL COMMENT '排序',
-                                  `memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '描述',
-                                  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Banner管理';
-
--- ----------------------------
--- Records of market_banner
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for member_address
--- ----------------------------
-DROP TABLE IF EXISTS `member_address`;
-CREATE TABLE `member_address`  (
-                                   `id` bigint NOT NULL AUTO_INCREMENT COMMENT '收件地址编号',
-                                   `user_id` bigint NOT NULL COMMENT '用户编号',
-                                   `name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收件人名称',
-                                   `mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '手机号',
-                                   `area_id` bigint NOT NULL COMMENT '地区编码',
-                                   `post_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮编',
-                                   `detail_address` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收件详细地址',
-                                   `defaulted` bit(1) NOT NULL COMMENT '是否默认',
-                                   `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-                                   `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                   `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-                                   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                   PRIMARY KEY (`id`) USING BTREE,
-                                   INDEX `idx_userId`(`user_id` ASC) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户收件地址';
-
--- ----------------------------
--- Records of member_address
--- ----------------------------
-BEGIN;
-INSERT INTO `member_address` (`id`, `user_id`, `name`, `mobile`, `area_id`, `post_code`, `detail_address`, `defaulted`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (21, 1, 'yunai', '15601691300', 610632, '200000', '芋道源码 233 号 666 室', b'1', '1', '2022-08-01 22:46:35', '1', '2022-08-01 22:46:35', b'0', 1);
-COMMIT;
-
--- ----------------------------
--- Table structure for product_brand
--- ----------------------------
-DROP TABLE IF EXISTS `product_brand`;
-CREATE TABLE `product_brand`  (
-                                  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '品牌编号',
-                                  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌名称',
-                                  `pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌图片',
-                                  `sort` int NULL DEFAULT 0 COMMENT '品牌排序',
-                                  `description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '品牌描述',
-                                  `status` tinyint NOT NULL COMMENT '状态',
-                                  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-                                  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-                                  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商品品牌';
-
--- ----------------------------
--- Records of product_brand
--- ----------------------------
-BEGIN;
-INSERT INTO `product_brand` (`id`, `name`, `pic_url`, `sort`, `description`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '苹果', 'http://test.yudao.iocoder.cn/e3726713fa56db5717c78c011762fcc7a251db12735c3581470638b8e1fa17e2.jpeg', 0, '是上市', 0, '1', '2022-07-30 22:12:18', '1', '2022-07-30 22:13:55', b'0', 1);
-COMMIT;
-
--- ----------------------------
--- Table structure for product_category
--- ----------------------------
-DROP TABLE IF EXISTS `product_category`;
-CREATE TABLE `product_category`  (
-                                     `id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号',
-                                     `parent_id` bigint NOT NULL COMMENT '父分类编号',
-                                     `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类名称',
-                                     `pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类图片',
-                                     `sort` int NULL DEFAULT 0 COMMENT '分类排序',
-                                     `description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '分类描述',
-                                     `status` tinyint NOT NULL COMMENT '开启状态',
-                                     `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-                                     `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                     `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-                                     `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                     `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                     `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                     PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商品分类';
-
--- ----------------------------
--- Records of product_category
--- ----------------------------
-BEGIN;
-INSERT INTO `product_category` (`id`, `parent_id`, `name`, `pic_url`, `sort`, `description`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 0, '电脑办公', 'http://test.yudao.iocoder.cn/122d548e1b3cd5dec72fe8075c6977a70f9cc13541a684ab3685f1b5df42f6bd.jpeg', 1, '1234', 0, '1', '2022-07-30 16:36:35', '1', '2022-07-30 20:27:16', b'0', 1), (2, 1, '笔记本', 'http://test.yudao.iocoder.cn/72713ac7b947600a019a18786ed0e6562e8692e253dbd35110a0a85c2469bbec.jpg', 1, '<p>测试一下</p>', 0, '1', '2022-07-30 16:38:09', '1', '2022-07-30 16:38:09', b'0', 1), (3, 1, '游戏本', 'http://test.yudao.iocoder.cn/287c50dd9f5f575f57329a0c57b2095be6d1aeba83867b905fe549f54a296feb.jpg', 2, '<p>测试一下</p>', 0, '1', '2022-07-30 16:39:09', '1', '2022-07-30 20:26:59', b'0', 1), (4, 0, '手机', 'http://test.yudao.iocoder.cn/e1b63900c78dbb661b3e383960cee5cfea7e1dd2fb22cff2e317ff025faaf8b2.jpeg', 2, '<p>123</p>', 0, '1', '2022-07-30 16:40:00', '1', '2022-07-30 16:40:09', b'0', 1), (5, 4, '5G手机', 'http://test.yudao.iocoder.cn/3af6557ac7def6423f046f5b2e920b644793420b466959aaa996a2e19068bbde.jpeg', 1, '<p><br></p>', 0, '1', '2022-07-30 16:43:00', '1', '2022-07-30 16:43:00', b'0', 1), (6, 4, '游戏手机', 'http://test.yudao.iocoder.cn/964fe9ccd1710d64ede261dc36d231918a017641986c15293c367f9f66d94d05.jpeg', 2, NULL, 0, '1', '2022-07-30 16:43:44', '1', '2022-07-30 16:43:44', b'0', 1), (7, 5, '厉害的 5G 手机', 'http://test.yudao.iocoder.cn/b287122f277838e8de368769b96217918605743bc45f3a29bda3cc7359dc66e1.png', 0, '123', 0, '1', '2022-07-30 20:38:09', '1', '2022-07-30 20:38:09', b'0', 1);
-COMMIT;
-
--- ----------------------------
--- Table structure for product_property
--- ----------------------------
-DROP TABLE IF EXISTS `product_property`;
-CREATE TABLE `product_property`  (
-                                     `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
-                                     `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格名称',
-                                     `status` tinyint NULL DEFAULT NULL COMMENT '状态: 0 开启 ,1 禁用',
-                                     `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                     `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                     `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
-                                     `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
-                                     `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                     `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                     PRIMARY KEY (`id`) USING BTREE,
-                                     INDEX `idx_name`(`name`(32) ASC) USING BTREE COMMENT '规格名称索引'
-) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '规格名称';
-
--- ----------------------------
--- Records of product_property
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for product_property_value
--- ----------------------------
-DROP TABLE IF EXISTS `product_property_value`;
-CREATE TABLE `product_property_value`  (
-                                           `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
-                                           `property_id` bigint NULL DEFAULT NULL COMMENT '规格键id',
-                                           `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格值名字',
-                                           `status` tinyint NULL DEFAULT NULL COMMENT '状态: 1 开启 ,2 禁用',
-                                           `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                           `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                           `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
-                                           `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
-                                           `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                           `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                           PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '规格值';
-
--- ----------------------------
--- Records of product_property_value
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for product_sku
--- ----------------------------
-DROP TABLE IF EXISTS `product_sku`;
-CREATE TABLE `product_sku` (
-                               `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
-                               `spu_id` bigint NOT NULL COMMENT 'spu编号',
-                               `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
-                               `name` varchar(128)  DEFAULT NULL COMMENT '商品 SKU 名字',
-                               `properties` varchar(128)  DEFAULT NULL COMMENT '规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }]',
-                               `price` int NOT NULL DEFAULT '-1' COMMENT '销售价格,单位:分',
-                               `market_price` int DEFAULT NULL COMMENT '市场价',
-                               `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分',
-                               `pic_url` varchar(128)  NOT NULL COMMENT '图片地址',
-                               `stock` int DEFAULT NULL COMMENT '库存',
-                               `warn_stock` int DEFAULT NULL COMMENT '预警库存',
-                               `volume` double DEFAULT NULL COMMENT '商品体积',
-                               `weight` double DEFAULT NULL COMMENT '商品重量',
-                               `bar_code` varchar(64)  DEFAULT NULL COMMENT '条形码',
-                               `status` tinyint DEFAULT NULL COMMENT '状态: 0-正常 1-禁用',
-                               `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                               `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                               `creator` varchar(64) DEFAULT NULL COMMENT '创建人',
-                               `updater` double(64,0) DEFAULT NULL COMMENT '更新人',
-`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB COMMENT='商品sku';
-
--- ----------------------------
--- Records of product_sku
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for product_spu
--- ----------------------------
-DROP TABLE IF EXISTS `product_spu`;
-CREATE TABLE `product_spu` (
-                               `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
-                               `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
-                               `brand_id` bigint DEFAULT NULL COMMENT '商品品牌编号',
-                               `category_id` bigint NOT NULL COMMENT '分类id',
-                               `spec_type` int NOT NULL COMMENT '规格类型:0 单规格 1 多规格',
-                               `code` varchar(128)  DEFAULT NULL COMMENT '商品编码',
-                               `name` varchar(128)  NOT NULL COMMENT '商品名称',
-                               `sell_point` varchar(128)  DEFAULT NULL COMMENT '卖点',
-                               `description` text  COMMENT '描述',
-                               `pic_urls` varchar(1024)  DEFAULT '' COMMENT '商品轮播图地址\n 数组,以逗号分隔\n 最多上传15张',
-                               `video_url` varchar(128)  DEFAULT NULL COMMENT '商品视频',
-                               `market_price` int DEFAULT NULL COMMENT '市场价,单位使用:分',
-                               `min_price` int DEFAULT NULL COMMENT '最小价格,单位使用:分',
-                               `max_price` int DEFAULT NULL COMMENT '最大价格,单位使用:分',
-                               `total_stock` int NOT NULL DEFAULT '0' COMMENT '总库存',
-                               `show_stock` int DEFAULT '0' COMMENT '是否展示库存',
-                               `sales_count` int DEFAULT '0' COMMENT '商品销量',
-                               `virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量',
-                               `click_count` int DEFAULT '0' COMMENT '商品点击量',
-                               `status` bit(1) DEFAULT NULL COMMENT '上下架状态: 0 上架(开启) 1 下架(禁用)-1 回收',
-                               `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',
-                               `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                               `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                               `creator` varchar(64)  DEFAULT NULL COMMENT '创建人',
-                               `updater` varchar(64)  DEFAULT NULL COMMENT '更新人',
-                               `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                               PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB COMMENT='商品spu';
-
--- ----------------------------
--- Records of product_spu
--- ----------------------------
-BEGIN;
-COMMIT;
-
-SET FOREIGN_KEY_CHECKS = 1;
-
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2000, '商品中心', '', 1, 60, 0, '/product', 'merchant', NULL, 0, b'1', b'1', '', '2022-07-29 15:53:53', '1', '2022-07-30 22:26:19', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2002, '商品分类', '', 2, 2, 2000, 'category', 'dict', 'mall/product/category/index', 0, b'1', b'1', '', '2022-07-29 15:53:53', '1', '2022-07-30 22:23:37', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2003, '分类查询', 'product:category:query', 3, 1, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2004, '分类创建', 'product:category:create', 3, 2, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2005, '分类更新', 'product:category:update', 3, 3, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2006, '分类删除', 'product:category:delete', 3, 4, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2007, '分类导出', 'product:category:export', 3, 5, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-30 13:52:13', b'1');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2008, '商品品牌', '', 2, 1, 2000, 'brand', 'dashboard', 'mall/product/brand/index', 0, b'1', b'1', '', '2022-07-30 13:52:44', '1', '2022-07-30 22:23:43', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2009, '品牌查询', 'product:brand:query', 3, 1, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2010, '品牌创建', 'product:brand:create', 3, 2, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2011, '品牌更新', 'product:brand:update', 3, 3, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2012, '品牌删除', 'product:brand:delete', 3, 4, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2013, '品牌导出', 'product:brand:export', 3, 5, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 14:15:00', b'1');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2014, '商品管理', '', 2, 0, 2000, 'spu', 'link', 'mall/product/spu/index', 0, b'1', b'1', '', '2022-07-30 14:22:58', '1', '2022-07-30 22:26:35', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2015, '商品查询', 'product:spu:query', 3, 1, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2016, '商品创建', 'product:spu:create', 3, 2, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2017, '商品更新', 'product:spu:update', 3, 3, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, '商品删除', 'product:spu:delete', 3, 4, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2019, '规格管理', '', 2, 3, 2000, 'property', '', 'mall/product/property/index', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2020, '规格查询', 'product:property:query', 3, 1, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2021, '规格创建', 'product:property:create', 3, 2, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2022, '规格更新', 'product:property:update', 3, 3, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2023, '规格删除', 'product:property:delete', 3, 4, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2024, '规格导出', 'product:property:export', 3, 5, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2025, 'Banner管理', '', 2, 1, 2000, 'banner', '', 'mall/market/banner/index', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2026, 'Banner查询', 'market:banner:query', 3, 1, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 'Banner创建', 'market:banner:create', 3, 2, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 'Banner更新', 'market:banner:update', 3, 3, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
-INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2029, 'Banner删除', 'market:banner:delete', 3, 4, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
-

+ 0 - 78
sql/optional/mall/order.sql

@@ -1,78 +0,0 @@
-/**todo cancelType 设置默认值 0?*/
-DROP TABLE IF EXISTS `trade_order`;
-CREATE TABLE `trade_order`
-(
-    `id`                      bigint          NOT NULL AUTO_INCREMENT COMMENT '用户ID',
-    `sn`                      varchar(32)     NOT NULL COMMENT '订单流水号',
-    `type`                    int             NOT NULL DEFAULT '0' COMMENT '订单类型:[0:普通订单 1:秒杀订单 2:拼团订单 3:砍价订单]',
-    `terminal`                int             NOT NULL COMMENT '订单来源终端:[1:小程序 2:H5 3:iOS 4:安卓]',
-    `user_id`                 bigint unsigned NOT NULL COMMENT '用户编号',
-    `user_ip`                 varchar(30)     NOT NULL DEFAULT '' COMMENT '用户 IP',
-    `user_remark`             varchar(200)             DEFAULT NULL COMMENT '用户备注',
-    `status`                  int             NOT NULL DEFAULT '0' COMMENT '订单状态:[0:待付款 1:待发货 2:待收货 3:已完成 4:已关闭]',
-    `product_count`           int             NOT NULL COMMENT '购买的商品数量',
-    `cancel_type`             int             DEFAULT NULL COMMENT '取消类型:[10:超时未支付 20:退款关闭 30:买家取消 40:已通过货到付款交易]',
-    `remark`                  varchar(200)             DEFAULT NULL COMMENT '商家备注',
-    `payed`                   bit(1)          NOT NULL DEFAULT b'0' COMMENT '是否已支付:[0:未支付 1:已经支付过]',
-    `pay_time`                datetime                 DEFAULT NULL COMMENT '订单支付时间',
-    `finish_time`             datetime                 DEFAULT NULL COMMENT '订单完成时间',
-    `cancel_time`             datetime                 DEFAULT NULL COMMENT '订单取消时间',
-    `sku_original_price`      int             NOT NULL DEFAULT '0' COMMENT '商品原价(总),单位:分',
-    `sku_promotion_price`     int             NOT NULL DEFAULT '0' COMMENT '商品优惠(总),单位:分',
-    `order_promotion_price`   int             NOT NULL DEFAULT '0' COMMENT '订单优惠(总),单位:分',
-    `delivery_price`          int             NOT NULL DEFAULT '0' COMMENT '运费金额,单位:分',
-    `pay_price`               int             NOT NULL DEFAULT '0' COMMENT '应付金额(总),单位:分',
-    `pay_order_id`            int             DEFAULT NULL COMMENT '支付订单编号',
-    `pay_channel`             int             DEFAULT NULL COMMENT '支付成功的支付渠道',
-    `delivery_type`           int             DEFAULT NULL DEFAULT '1' COMMENT '配送方式:[1:快递发货 2:自提]',
-    `actual_delivery_type`    int             DEFAULT NULL DEFAULT '1' COMMENT '实际的配送方式:[1:快递发货 2:自提]',
-    `delivery_template_id`     int                      DEFAULT NULL COMMENT '配置模板的编号',
-    `express_no`              int                      DEFAULT NULL COMMENT '物流公司单号',
-    `delivery_status`         bit(1)          NOT NULL DEFAULT b'0' COMMENT '发货状态[0:未发货 1:已发货]',
-    `delivery_time`           datetime                 DEFAULT NULL COMMENT '发货时间',
-    `receive_time`            datetime                 DEFAULT NULL COMMENT '收货时间',
-    `receiver_name`           varchar(20)     NOT NULL COMMENT '收件人名称',
-    `receiver_mobile`         varchar(20)     NOT NULL COMMENT '收件人手机',
-    `receiver_area_id`        int             NOT NULL COMMENT '收件人地区编号',
-    `receiver_post_code`      int                      DEFAULT NULL COMMENT '收件人邮编',
-    `receiver_detail_address` varchar(255)    NOT NULL COMMENT '收件人详细地址',
-    `refund_status`           int             NOT NULL DEFAULT '0' COMMENT '订单状态:[0:未退款 1:部分退款 2:全部退款]',
-    `refund_price`            int             NOT NULL DEFAULT '0' COMMENT '退款金额,单位:分',
-    `coupon_id`               bigint unsigned NOT NULL COMMENT '优惠劵编号',
-    `creator`                 varchar(64)              DEFAULT '' COMMENT '创建者',
-    `create_time`             datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    `updater`                 varchar(64)              DEFAULT '' COMMENT '更新者',
-    `update_time`             datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-    `deleted`                 bit(1)          NOT NULL DEFAULT b'0' COMMENT '是否删除',
-    PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB COMMENT ='交易订单表';
-
-
-DROP TABLE IF EXISTS `trade_order_item`;
-CREATE TABLE `trade_order_item`
-(
-    `id`                    bigint          NOT NULL AUTO_INCREMENT COMMENT '用户ID',
-    `user_id`               bigint unsigned NOT NULL COMMENT '用户编号',
-    `order_Id`              bigint unsigned NOT NULL COMMENT '订单编号',
-    `spu_id`                bigint unsigned NOT NULL COMMENT '商品 SPU 编号',
-    `sku_id`                bigint unsigned NOT NULL COMMENT '商品 SKU 编号',
-    `properties`            json                     DEFAULT NULL COMMENT '规格值数组,JSON 格式',
-    `name`                  varchar(128)    NOT NULL DEFAULT '' COMMENT '商品名称',
-    `pic_url`               varchar(200)             DEFAULT NULL COMMENT '商品图片',
-    `count`                 int             NOT NULL COMMENT '购买数量',
-    `commented`             bit(1)          NOT NULL DEFAULT b'0' COMMENT '是否评论:[0:未评论 1:已评论]',
-    `original_price`        int             NOT NULL DEFAULT '0' COMMENT '商品原价(单),单位:分',
-    `total_original_price`  int             NOT NULL DEFAULT '0' COMMENT '商品原价(总),单位:分',
-    `total_promotion_price` int             NOT NULL DEFAULT '0' COMMENT '商品级优惠(总),单位:分',
-    `present_price`         int             NOT NULL DEFAULT '0' COMMENT '最终购买金额(单),单位:分。',
-    `total_present_price`   int             NOT NULL DEFAULT '0' COMMENT '最终购买金额(总),单位:分。',
-    `total_pay_price`       int             NOT NULL DEFAULT '0' COMMENT '应付金额(总),单位:分',
-    `refund_status`         int             NOT NULL DEFAULT '0' COMMENT '退款状态:[0:未申请退款 1:申请退款 2:等待退款 3:退款成功]',
-    `refund_total`          int             NOT NULL DEFAULT '0' COMMENT '退款总金额,单位:分',
-    `creator`               varchar(64)              DEFAULT '' COMMENT '创建者',
-    `create_time`           datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    `updater`               varchar(64)              DEFAULT '' COMMENT '更新者',
-    `update_time`           datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-    `deleted`               bit(1)          NOT NULL DEFAULT b'0' COMMENT '是否删除',
-    PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB COMMENT ='交易订单明细表';

+ 47 - 22
yudao-dependencies/pom.xml

@@ -14,37 +14,39 @@
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <properties>
-        <revision>1.6.5-snapshot</revision>
+        <revision>1.7.0-snapshot</revision>
         <!-- 统一依赖管理 -->
-        <spring.boot.version>2.7.6</spring.boot.version>
+        <spring.boot.version>2.7.8</spring.boot.version>
         <!-- Web 相关 -->
-        <knife4j.version>3.0.3</knife4j.version>
+        <knife4j.version>4.0.0</knife4j.version>
         <swagger-annotations.version>1.6.8</swagger-annotations.version>
         <servlet.versoin>2.5</servlet.versoin>
         <!-- DB 相关 -->
         <druid.version>1.2.15</druid.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.6.0</dynamic-datasource.version>
+        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
+        <mybatis-plus-generator.version>3.5.3.1</mybatis-plus-generator.version>
+        <dynamic-datasource.version>3.6.1</dynamic-datasource.version>
         <redisson.version>3.18.0</redisson.version>
         <!-- 服务保障相关 -->
         <lock4j.version>2.2.3</lock4j.version>
         <resilience4j.version>1.7.1</resilience4j.version>
         <!-- 监控相关 -->
         <skywalking.version>8.12.0</skywalking.version>
-        <spring-boot-admin.version>2.7.9</spring-boot-admin.version>
+        <spring-boot-admin.version>2.7.10</spring-boot-admin.version>
         <opentracing.version>0.33.0</opentracing.version>
         <!-- Test 测试相关 -->
         <podam.version>7.2.11.RELEASE</podam.version>
         <jedis-mock.version>1.0.5</jedis-mock.version>
-        <mockito-inline.version>4.8.0</mockito-inline.version>
+        <mockito-inline.version>4.11.0</mockito-inline.version>
         <!-- Bpm 工作流相关 -->
-        <flowable.version>6.7.2</flowable.version>
+        <flowable.version>6.8.0</flowable.version>
         <!-- 工具类相关 -->
+        <captcha-plus.version>1.0.2</captcha-plus.version>
+        <jsoup.version>1.15.3</jsoup.version>
         <lombok.version>1.18.24</lombok.version>
         <mapstruct.version>1.5.3.Final</mapstruct.version>
-        <hutool.version>5.8.10</hutool.version>
-        <easyexcel.verion>3.1.3</easyexcel.verion>
+        <hutool.version>5.8.11</hutool.version>
+        <easyexcel.verion>3.2.0</easyexcel.verion>
         <velocity.version>2.3</velocity.version>
         <screw.version>1.0.5</screw.version>
         <fastjson.version>1.2.83</fastjson.version>
@@ -54,19 +56,19 @@
         <commons-net.version>3.8.0</commons-net.version>
         <jsch.version>0.1.55</jsch.version>
         <tika-core.version>2.6.0</tika-core.version>
-        <aj-captcha.version>1.3.0</aj-captcha.version>
-        <netty-all.version>4.1.85.Final</netty-all.version>
+        <netty-all.version>4.1.86.Final</netty-all.version>
         <ip2region.version>2.6.6</ip2region.version>
         <!-- 三方云服务相关 -->
         <okio.version>3.0.0</okio.version>
         <okhttp3.version>4.10.0</okhttp3.version>
-        <minio.version>8.4.6</minio.version>
+        <minio.version>8.5.1</minio.version>
         <aliyun-java-sdk-core.version>4.6.3</aliyun-java-sdk-core.version>
         <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
-        <tencentcloud-sdk-java.version>3.1.637</tencentcloud-sdk-java.version>
+        <tencentcloud-sdk-java.version>3.1.676</tencentcloud-sdk-java.version>
         <justauth.version>1.4.0</justauth.version>
         <jimureport.version>1.5.6</jimureport.version>
         <xercesImpl.version>2.12.2</xercesImpl.version>
+        <wx-java-mp.version>4.3.0</wx-java-mp.version>
     </properties>
 
     <dependencyManagement>
@@ -141,6 +143,11 @@
                 <artifactId>yudao-spring-boot-starter-captcha</artifactId>
                 <version>${revision}</version>
             </dependency>
+            <dependency>
+                <groupId>cn.iocoder.boot</groupId>
+                <artifactId>yudao-spring-boot-starter-desensitize</artifactId>
+                <version>${revision}</version>
+            </dependency>
 
             <!-- Spring 核心 -->
             <dependency>
@@ -165,7 +172,7 @@
 
             <dependency>
                 <groupId>com.github.xiaoymin</groupId>
-                <artifactId>knife4j-spring-boot-starter</artifactId>
+                <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
                 <version>${knife4j.version}</version>
                 <exclusions>
                     <exclusion>
@@ -446,12 +453,6 @@
                 <version>${tika-core.version}</version>
             </dependency>
 
-            <dependency>
-                <groupId>com.anji-plus</groupId>
-                <artifactId>spring-boot-starter-captcha</artifactId>
-                <version>${aj-captcha.version}</version>
-            </dependency>
-
             <dependency>
                 <groupId>org.apache.velocity</groupId>
                 <artifactId>velocity-engine-core</artifactId>
@@ -516,12 +517,24 @@
                 <version>${netty-all.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>com.xingyuv</groupId>
+                <artifactId>spring-boot-starter-captcha-plus</artifactId>
+                <version>${captcha-plus.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>org.lionsoul</groupId>
                 <artifactId>ip2region</artifactId>
                 <version>${ip2region.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>org.jsoup</groupId>
+                <artifactId>jsoup</artifactId>
+                <version>${jsoup.version}</version>
+            </dependency>
+
             <!-- 三方云服务相关 -->
             <dependency>
                 <groupId>com.squareup.okio</groupId>
@@ -578,6 +591,12 @@
                 <version>${justauth.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>com.github.binarywang</groupId>
+                <artifactId>wx-java-mp-spring-boot-starter</artifactId>
+                <version>${wx-java-mp.version}</version>
+            </dependency>
+
             <!-- 积木报表-->
             <dependency>
                 <groupId>org.jeecgframework.jimureport</groupId>
@@ -595,6 +614,12 @@
                 <artifactId>xercesImpl</artifactId>
                 <version>${xercesImpl.version}</version>
             </dependency>
+            <!-- SpringBoot Websocket -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-websocket</artifactId>
+                <version>${spring.boot.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 

+ 2 - 2
yudao-example/yudao-sso-demo-by-code/pom.xml

@@ -21,7 +21,7 @@
         <maven.compiler.target>8</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <!-- 统一依赖管理 -->
-        <spring.boot.version>2.7.6</spring.boot.version>
+        <spring.boot.version>2.7.8</spring.boot.version>
     </properties>
 
     <dependencyManagement>
@@ -52,7 +52,7 @@
         <dependency>
             <groupId>cn.hutool</groupId>
             <artifactId>hutool-all</artifactId>
-            <version>5.8.10</version>
+            <version>5.8.11</version>
         </dependency>
 
         <dependency>

+ 2 - 2
yudao-example/yudao-sso-demo-by-password/pom.xml

@@ -21,7 +21,7 @@
         <maven.compiler.target>8</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <!-- 统一依赖管理 -->
-        <spring.boot.version>2.7.6</spring.boot.version>
+        <spring.boot.version>2.7.8</spring.boot.version>
     </properties>
 
     <dependencyManagement>
@@ -52,7 +52,7 @@
         <dependency>
             <groupId>cn.hutool</groupId>
             <artifactId>hutool-all</artifactId>
-            <version>5.8.10</version>
+            <version>5.8.11</version>
         </dependency>
 
         <dependency>

+ 2 - 0
yudao-framework/pom.xml

@@ -40,6 +40,8 @@
 
         <module>yudao-spring-boot-starter-flowable</module>
         <module>yudao-spring-boot-starter-captcha</module>
+        <module>yudao-spring-boot-starter-websocket</module>
+        <module>yudao-spring-boot-starter-desensitize</module>
     </modules>
 
     <artifactId>yudao-framework</artifactId>

+ 5 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java

@@ -41,6 +41,11 @@ public class LocalDateTimeUtils {
         return LocalDateTime.of(year, mouth, day, 0, 0, 0);
     }
 
+    public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
+                                                   int year2, int mouth2, int day2) {
+        return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
+    }
+
     /**
      * 判断当前时间是否在该时间范围内
      *

+ 0 - 1
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.framework.common.util.validation;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.StrUtil;
 import org.springframework.util.StringUtils;
 
 import javax.validation.ConstraintViolation;

+ 4 - 0
yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java

@@ -40,6 +40,10 @@ public class BannerApplicationRunner implements ApplicationRunner {
             if (isNotPresent("cn.iocoder.yudao.framework.flowable.config.YudaoFlowableConfiguration")) {
                 System.out.println("[工作流模块 yudao-module-bpm - 已禁用][参考 https://doc.iocoder.cn/bpm/ 开启]");
             }
+            // 微信公众号
+            if (isNotPresent("cn.iocoder.yudao.module.mp.framework.mp.config.MpConfiguration")) {
+                System.out.println("[微信公众号 yudao-module-mp - 已禁用][参考 https://doc.iocoder.cn/mp/build/ 开启]");
+            }
         });
     }
 

+ 43 - 0
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java

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

+ 15 - 0
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtilsTest.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.framework.datapermission.core.util;
+
+import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class DataPermissionUtilsTest {
+
+    @Test
+    public void testExecuteIgnore() {
+        DataPermissionUtils.executeIgnore(() -> assertFalse(DataPermissionContextHolder.get().enable()));
+    }
+
+}

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java

@@ -49,7 +49,7 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {
         log.info("[execute][解析到错误码数量为 ({}) 个]", autoGenerateDTOs.size());
 
         // 第二步,写入到 system 服务
-        errorCodeApi.autoGenerateErrorCodes(autoGenerateDTOs);
+        errorCodeApi.autoGenerateErrorCodeList(autoGenerateDTOs);
         log.info("[execute][写入到 system 组件完成]");
     }
 

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml

@@ -52,7 +52,7 @@
         <dependency>
             <groupId>com.alipay.sdk</groupId>
             <artifactId>alipay-sdk-java</artifactId>
-            <version>4.35.0.ALL</version>
+            <version>4.35.32.ALL</version>
             <exclusions>
                 <exclusion>
                     <groupId>org.bouncycastle</groupId>

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

@@ -32,7 +32,7 @@ public class TenantFrameworkServiceImpl implements TenantFrameworkService {
 
                 @Override
                 public List<Long> load(Object key) {
-                    return tenantApi.getTenantIds();
+                    return tenantApi.getTenantIdList();
                 }
 
             });
@@ -47,7 +47,7 @@ public class TenantFrameworkServiceImpl implements TenantFrameworkService {
                 @Override
                 public ServiceException load(Long id) {
                     try {
-                        tenantApi.validTenant(id);
+                        tenantApi.validateTenant(id);
                         return SERVICE_EXCEPTION_NULL;
                     } catch (ServiceException ex) {
                         return ex;

+ 26 - 0
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.tenant.core.util;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 
 import java.util.Map;
+import java.util.concurrent.Callable;
 
 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
 
@@ -36,6 +37,31 @@ public class TenantUtils {
         }
     }
 
+    /**
+     * 使用指定租户,执行对应的逻辑
+     *
+     * 注意,如果当前是忽略租户的情况下,会被强制设置成不忽略租户
+     * 当然,执行完成后,还是会恢复回去
+     *
+     * @param tenantId 租户编号
+     * @param callable 逻辑
+     */
+    public static <V> V execute(Long tenantId, Callable<V> callable) {
+        Long oldTenantId = TenantContextHolder.getTenantId();
+        Boolean oldIgnore = TenantContextHolder.isIgnore();
+        try {
+            TenantContextHolder.setTenantId(tenantId);
+            TenantContextHolder.setIgnore(false);
+            // 执行逻辑
+            return callable.call();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            TenantContextHolder.setTenantId(oldTenantId);
+            TenantContextHolder.setIgnore(oldIgnore);
+        }
+    }
+
     /**
      * 忽略租户,执行对应的逻辑
      *

+ 1 - 0
yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml

@@ -34,6 +34,7 @@
         <!-- 三方云服务相关 -->
         <dependency>
             <groupId>com.github.binarywang</groupId>
+<!--            <artifactId>weixin-java-mp</artifactId>-->
             <artifactId>wx-java-mp-spring-boot-starter</artifactId>
             <version>4.4.0</version>
         </dependency>

+ 4 - 5
yudao-framework/yudao-spring-boot-starter-captcha/pom.xml

@@ -17,6 +17,10 @@
     </description>
 
     <dependencies>
+        <dependency>
+            <groupId>com.xingyuv</groupId>
+            <artifactId>spring-boot-starter-captcha-plus</artifactId>
+        </dependency>
         <!-- Spring 核心 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -29,11 +33,6 @@
             <artifactId>yudao-spring-boot-starter-redis</artifactId>
         </dependency>
 
-        <!-- 验证码相关 -->
-        <dependency>
-            <groupId>com.anji-plus</groupId>
-            <artifactId>spring-boot-starter-captcha</artifactId>
-        </dependency>
     </dependencies>
 
 </project>

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.captcha.config;
 import cn.hutool.core.util.ClassUtil;
 import cn.iocoder.yudao.framework.captcha.core.enums.CaptchaRedisKeyConstants;
 import cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl;
-import com.anji.captcha.service.CaptchaCacheService;
+import com.xingyuv.captcha.service.CaptchaCacheService;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.context.annotation.Bean;
 import org.springframework.data.redis.core.StringRedisTemplate;

+ 1 - 4
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java

@@ -1,13 +1,10 @@
 package cn.iocoder.yudao.framework.captcha.core.enums;
 
 import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
-import com.anji.captcha.model.vo.PointVO;
-import org.redisson.api.RLock;
+import com.xingyuv.captcha.model.vo.PointVO;
 
 import java.time.Duration;
-import java.util.Map;
 
-import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.HASH;
 import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
 
 /**

+ 1 - 2
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/service/RedisCaptchaServiceImpl.java

@@ -1,9 +1,8 @@
 package cn.iocoder.yudao.framework.captcha.core.service;
 
-import com.anji.captcha.service.CaptchaCacheService;
+import com.xingyuv.captcha.service.CaptchaCacheService;
 import lombok.AllArgsConstructor;
 import lombok.NoArgsConstructor;
-import lombok.RequiredArgsConstructor;
 import org.springframework.data.redis.core.StringRedisTemplate;
 
 import javax.annotation.Resource;

+ 0 - 0
yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService → yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/META-INF/services/com.xingyuv.captcha.service.CaptchaCacheService


+ 38 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/pom.xml

@@ -0,0 +1,38 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>yudao-framework</artifactId>
+        <groupId>cn.iocoder.boot</groupId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>yudao-spring-boot-starter-desensitize</artifactId>
+    <description>脱敏组件:支持 JSON 返回数据时,将邮箱、手机等字段进行脱敏</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-common</artifactId>
+        </dependency>
+
+        <!-- jackson -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

+ 32 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.framework.desensitize.core.base.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
+import cn.iocoder.yudao.framework.desensitize.core.base.serializer.StringDesensitizeSerializer;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 顶级脱敏注解,自定义注解需要使用此注解
+ *
+ * @author gaibu
+ */
+@Documented
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside // 此注解是其他所有 jackson 注解的元注解,打上了此注解的注解表明是 jackson 注解的一部分
+@JsonSerialize(using = StringDesensitizeSerializer.class) // 指定序列化器
+public @interface DesensitizeBy {
+
+    /**
+     * 脱敏处理器
+     */
+    @SuppressWarnings("rawtypes")
+    Class<? extends DesensitizationHandler> handler();
+
+}

+ 21 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/handler/DesensitizationHandler.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.framework.desensitize.core.base.handler;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * 脱敏处理器接口
+ *
+ * @author gaibu
+ */
+public interface DesensitizationHandler<T extends Annotation> {
+
+    /**
+     * 脱敏
+     *
+     * @param origin     原始字符串
+     * @param annotation 注解信息
+     * @return 脱敏后的字符串
+     */
+    String desensitize(String origin, T annotation);
+
+}

+ 92 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java

@@ -0,0 +1,92 @@
+package cn.iocoder.yudao.framework.desensitize.core.base.serializer;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.lang.Singleton;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+/**
+ * 脱敏序列化器
+ *
+ * 实现 JSON 返回数据时,使用 {@link DesensitizationHandler} 对声明脱敏注解的字段,进行脱敏处理。
+ *
+ * @author gaibu
+ */
+@SuppressWarnings("rawtypes")
+public class StringDesensitizeSerializer extends StdSerializer<String> implements ContextualSerializer {
+
+    @Getter
+    @Setter
+    private DesensitizationHandler desensitizationHandler;
+
+    protected StringDesensitizeSerializer() {
+        super(String.class);
+    }
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) {
+        DesensitizeBy annotation = beanProperty.getAnnotation(DesensitizeBy.class);
+        if (annotation == null) {
+            return this;
+        }
+        // 创建一个 StringDesensitizeSerializer 对象,使用 DesensitizeBy 对应的处理器
+        StringDesensitizeSerializer serializer = new StringDesensitizeSerializer();
+        serializer.setDesensitizationHandler(Singleton.get(annotation.handler()));
+        return serializer;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void serialize(String value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
+        if (StrUtil.isBlank(value)) {
+            gen.writeNull();
+            return;
+        }
+        // 获取序列化字段
+        Field field = getField(gen);
+
+        // 自定义处理器
+        DesensitizeBy[] annotations = AnnotationUtil.getCombinationAnnotations(field, DesensitizeBy.class);
+        if (ArrayUtil.isEmpty(annotations)) {
+            gen.writeString(value);
+            return;
+        }
+        for (Annotation annotation : field.getAnnotations()) {
+            if (AnnotationUtil.hasAnnotation(annotation.annotationType(), DesensitizeBy.class)) {
+                value = this.desensitizationHandler.desensitize(value, annotation);
+                gen.writeString(value);
+                return;
+            }
+        }
+        gen.writeString(value);
+    }
+
+    /**
+     * 获取字段
+     *
+     * @param generator JsonGenerator
+     * @return 字段
+     */
+    private Field getField(JsonGenerator generator) {
+        String currentName = generator.getOutputContext().getCurrentName();
+        Object currentValue = generator.getCurrentValue();
+        Class<?> currentValueClass = currentValue.getClass();
+        return ReflectUtil.getField(currentValueClass, currentName);
+    }
+
+}

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

@@ -0,0 +1,4 @@
+/**
+ * 脱敏组件:支持 JSON 返回数据时,将邮箱、手机等字段进行脱敏
+ */
+package cn.iocoder.yudao.framework.desensitize.core;

+ 36 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/EmailDesensitize.java

@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.framework.desensitize.core.regex.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.regex.handler.EmailDesensitizationHandler;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 邮箱脱敏注解
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = EmailDesensitizationHandler.class)
+public @interface EmailDesensitize {
+
+    /**
+     * 匹配的正则表达式
+     */
+    String regex() default "(^.)[^@]*(@.*$)";
+
+    /**
+     * 替换规则,邮箱;
+     *
+     * 比如:example@gmail.com 脱敏之后为 e****@gmail.com
+     */
+    String replacer() default "$1****$2";
+}

+ 38 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/RegexDesensitize.java

@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.framework.desensitize.core.regex.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.regex.handler.DefaultRegexDesensitizationHandler;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 正则脱敏注解
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = DefaultRegexDesensitizationHandler.class)
+public @interface RegexDesensitize {
+
+    /**
+     * 匹配的正则表达式(默认匹配所有)
+     */
+    String regex() default "^[\\s\\S]*$";
+
+    /**
+     * 替换规则,会将匹配到的字符串全部替换成 replacer
+     *
+     * 例如:regex=123; replacer=******
+     * 原始字符串 123456789
+     * 脱敏后字符串 ******456789
+     */
+    String replacer() default "******";
+}

+ 38 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java

@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.framework.desensitize.core.regex.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * 正则表达式脱敏处理器抽象类,已实现通用的方法
+ *
+ * @author gaibu
+ */
+public abstract class AbstractRegexDesensitizationHandler<T extends Annotation>
+        implements DesensitizationHandler<T> {
+
+    @Override
+    public String desensitize(String origin, T annotation) {
+        String regex = getRegex(annotation);
+        String replacer = getReplacer(annotation);
+        return origin.replaceAll(regex, replacer);
+    }
+
+    /**
+     * 获取注解上的 regex 参数
+     *
+     * @param annotation 注解信息
+     * @return 正则表达式
+     */
+    abstract String getRegex(T annotation);
+
+    /**
+     * 获取注解上的 replacer 参数
+     *
+     * @param annotation 注解信息
+     * @return 待替换的字符串
+     */
+    abstract String getReplacer(T annotation);
+
+}

+ 21 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.framework.desensitize.core.regex.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.RegexDesensitize;
+
+/**
+ * {@link RegexDesensitize} 的正则脱敏处理器
+ *
+ * @author gaibu
+ */
+public class DefaultRegexDesensitizationHandler extends AbstractRegexDesensitizationHandler<RegexDesensitize> {
+
+    @Override
+    String getRegex(RegexDesensitize annotation) {
+        return annotation.regex();
+    }
+
+    @Override
+    String getReplacer(RegexDesensitize annotation) {
+        return annotation.replacer();
+    }
+}

+ 22 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.framework.desensitize.core.regex.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.EmailDesensitize;
+
+/**
+ * {@link EmailDesensitize} 的脱敏处理器
+ *
+ * @author gaibu
+ */
+public class EmailDesensitizationHandler extends AbstractRegexDesensitizationHandler<EmailDesensitize> {
+
+    @Override
+    String getRegex(EmailDesensitize annotation) {
+        return annotation.regex();
+    }
+
+    @Override
+    String getReplacer(EmailDesensitize annotation) {
+        return annotation.replacer();
+    }
+
+}

+ 40 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/BankCardDesensitize.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.slider.handler.BankCardDesensitization;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 银行卡号
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = BankCardDesensitization.class)
+public @interface BankCardDesensitize {
+
+    /**
+     * 前缀保留长度
+     */
+    int prefixKeep() default 6;
+
+    /**
+     * 后缀保留长度
+     */
+    int suffixKeep() default 2;
+
+    /**
+     * 替换规则,银行卡号; 比如:9988002866797031 脱敏之后为 998800********31
+     */
+    String replacer() default "*";
+
+}

+ 40 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.slider.handler.CarLicenseDesensitization;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 车牌号
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = CarLicenseDesensitization.class)
+public @interface CarLicenseDesensitize {
+
+    /**
+     * 前缀保留长度
+     */
+    int prefixKeep() default 3;
+
+    /**
+     * 后缀保留长度
+     */
+    int suffixKeep() default 1;
+
+    /**
+     * 替换规则,车牌号;比如:粤A66666 脱敏之后为粤A6***6
+     */
+    String replacer() default "*";
+
+}

+ 40 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.slider.handler.ChineseNameDesensitization;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 中文名
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = ChineseNameDesensitization.class)
+public @interface ChineseNameDesensitize {
+
+    /**
+     * 前缀保留长度
+     */
+    int prefixKeep() default 1;
+
+    /**
+     * 后缀保留长度
+     */
+    int suffixKeep() default 0;
+
+    /**
+     * 替换规则,中文名;比如:刘子豪脱敏之后为刘**
+     */
+    String replacer() default "*";
+
+}

+ 40 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.slider.handler.FixedPhoneDesensitization;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 固定电话
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = FixedPhoneDesensitization.class)
+public @interface FixedPhoneDesensitize {
+
+    /**
+     * 前缀保留长度
+     */
+    int prefixKeep() default 4;
+
+    /**
+     * 后缀保留长度
+     */
+    int suffixKeep() default 2;
+
+    /**
+     * 替换规则,固定电话;比如:01086551122 脱敏之后为 0108*****22
+     */
+    String replacer() default "*";
+
+}

+ 40 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/IdCardDesensitize.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.slider.handler.IdCardDesensitization;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 身份证
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = IdCardDesensitization.class)
+public @interface IdCardDesensitize {
+
+    /**
+     * 前缀保留长度
+     */
+    int prefixKeep() default 6;
+
+    /**
+     * 后缀保留长度
+     */
+    int suffixKeep() default 2;
+
+    /**
+     * 替换规则,身份证号码;比如:530321199204074611 脱敏之后为 530321**********11
+     */
+    String replacer() default "*";
+
+}

+ 40 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/MobileDesensitize.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.slider.handler.MobileDesensitization;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 手机号
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = MobileDesensitization.class)
+public @interface MobileDesensitize {
+
+    /**
+     * 前缀保留长度
+     */
+    int prefixKeep() default 3;
+
+    /**
+     * 后缀保留长度
+     */
+    int suffixKeep() default 4;
+
+    /**
+     * 替换规则,手机号;比如:13248765917 脱敏之后为 132****5917
+     */
+    String replacer() default "*";
+
+}

+ 42 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/PasswordDesensitize.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.slider.handler.PasswordDesensitization;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 密码
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = PasswordDesensitization.class)
+public @interface PasswordDesensitize {
+
+    /**
+     * 前缀保留长度
+     */
+    int prefixKeep() default 0;
+
+    /**
+     * 后缀保留长度
+     */
+    int suffixKeep() default 0;
+
+    /**
+     * 替换规则,密码;
+     *
+     * 比如:123456 脱敏之后为 ******
+     */
+    String replacer() default "*";
+
+}

+ 43 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/SliderDesensitize.java

@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.slider.handler.DefaultDesensitizationHandler;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 滑动脱敏注解
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = DefaultDesensitizationHandler.class)
+public @interface SliderDesensitize {
+
+    /**
+     * 后缀保留长度
+     */
+    int suffixKeep() default 0;
+
+    /**
+     * 替换规则,会将前缀后缀保留后,全部替换成 replacer
+     *
+     * 例如:prefixKeep = 1; suffixKeep = 2; replacer = "*";
+     * 原始字符串  123456
+     * 脱敏后     1***56
+     */
+    String replacer() default "*";
+
+    /**
+     * 前缀保留长度
+     */
+    int prefixKeep() default 0;
+}

+ 78 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java

@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * 滑动脱敏处理器抽象类,已实现通用的方法
+ *
+ * @author gaibu
+ */
+public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
+        implements DesensitizationHandler<T> {
+
+    @Override
+    public String desensitize(String origin, T annotation) {
+        int prefixKeep = getPrefixKeep(annotation);
+        int suffixKeep = getSuffixKeep(annotation);
+        String replacer = getReplacer(annotation);
+        int length = origin.length();
+
+        // 情况一:原始字符串长度小于等于保留长度,则原始字符串全部替换
+        if (prefixKeep >= length || suffixKeep >= length) {
+            return buildReplacerByLength(replacer, length);
+        }
+
+        // 情况二:原始字符串长度小于等于前后缀保留字符串长度,则原始字符串全部替换
+        if ((prefixKeep + suffixKeep) >= length) {
+            return buildReplacerByLength(replacer, length);
+        }
+
+        // 情况三:原始字符串长度大于前后缀保留字符串长度,则替换中间字符串
+        int interval = length - prefixKeep - suffixKeep;
+        return origin.substring(0, prefixKeep) +
+                buildReplacerByLength(replacer, interval) +
+                origin.substring(prefixKeep + interval);
+    }
+
+    /**
+     * 根据长度循环构建替换符
+     *
+     * @param replacer 替换符
+     * @param length   长度
+     * @return 构建后的替换符
+     */
+    private String buildReplacerByLength(String replacer, int length) {
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0; i < length; i++) {
+            builder.append(replacer);
+        }
+        return builder.toString();
+    }
+
+    /**
+     * 前缀保留长度
+     *
+     * @param annotation 注解信息
+     * @return 前缀保留长度
+     */
+    abstract Integer getPrefixKeep(T annotation);
+
+    /**
+     * 后缀保留长度
+     *
+     * @param annotation 注解信息
+     * @return 后缀保留长度
+     */
+    abstract Integer getSuffixKeep(T annotation);
+
+    /**
+     * 替换符
+     *
+     * @param annotation 注解信息
+     * @return 替换符
+     */
+    abstract String getReplacer(T annotation);
+
+}

+ 27 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/BankCardDesensitization.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.BankCardDesensitize;
+
+/**
+ * {@link BankCardDesensitize} 的脱敏处理器
+ *
+ * @author gaibu
+ */
+public class BankCardDesensitization extends AbstractSliderDesensitizationHandler<BankCardDesensitize> {
+
+    @Override
+    Integer getPrefixKeep(BankCardDesensitize annotation) {
+        return annotation.prefixKeep();
+    }
+
+    @Override
+    Integer getSuffixKeep(BankCardDesensitize annotation) {
+        return annotation.suffixKeep();
+    }
+
+    @Override
+    String getReplacer(BankCardDesensitize annotation) {
+        return annotation.replacer();
+    }
+
+}

+ 25 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.CarLicenseDesensitize;
+
+/**
+ * {@link CarLicenseDesensitize} 的脱敏处理器
+ *
+ * @author gaibu
+ */
+public class CarLicenseDesensitization extends AbstractSliderDesensitizationHandler<CarLicenseDesensitize> {
+    @Override
+    Integer getPrefixKeep(CarLicenseDesensitize annotation) {
+        return annotation.prefixKeep();
+    }
+
+    @Override
+    Integer getSuffixKeep(CarLicenseDesensitize annotation) {
+        return annotation.suffixKeep();
+    }
+
+    @Override
+    String getReplacer(CarLicenseDesensitize annotation) {
+        return annotation.replacer();
+    }
+}

+ 27 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.ChineseNameDesensitize;
+
+/**
+ * {@link ChineseNameDesensitize} 的脱敏处理器
+ *
+ * @author gaibu
+ */
+public class ChineseNameDesensitization extends AbstractSliderDesensitizationHandler<ChineseNameDesensitize> {
+
+    @Override
+    Integer getPrefixKeep(ChineseNameDesensitize annotation) {
+        return annotation.prefixKeep();
+    }
+
+    @Override
+    Integer getSuffixKeep(ChineseNameDesensitize annotation) {
+        return annotation.suffixKeep();
+    }
+
+    @Override
+    String getReplacer(ChineseNameDesensitize annotation) {
+        return annotation.replacer();
+    }
+
+}

+ 25 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.SliderDesensitize;
+
+/**
+ * {@link SliderDesensitize} 的脱敏处理器
+ *
+ * @author gaibu
+ */
+public class DefaultDesensitizationHandler extends AbstractSliderDesensitizationHandler<SliderDesensitize> {
+    @Override
+    Integer getPrefixKeep(SliderDesensitize annotation) {
+        return annotation.prefixKeep();
+    }
+
+    @Override
+    Integer getSuffixKeep(SliderDesensitize annotation) {
+        return annotation.suffixKeep();
+    }
+
+    @Override
+    String getReplacer(SliderDesensitize annotation) {
+        return annotation.replacer();
+    }
+}

+ 25 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.FixedPhoneDesensitize;
+
+/**
+ * {@link FixedPhoneDesensitize} 的脱敏处理器
+ *
+ * @author gaibu
+ */
+public class FixedPhoneDesensitization extends AbstractSliderDesensitizationHandler<FixedPhoneDesensitize> {
+    @Override
+    Integer getPrefixKeep(FixedPhoneDesensitize annotation) {
+        return annotation.prefixKeep();
+    }
+
+    @Override
+    Integer getSuffixKeep(FixedPhoneDesensitize annotation) {
+        return annotation.suffixKeep();
+    }
+
+    @Override
+    String getReplacer(FixedPhoneDesensitize annotation) {
+        return annotation.replacer();
+    }
+}

+ 25 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/IdCardDesensitization.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.IdCardDesensitize;
+
+/**
+ * {@link IdCardDesensitize} 的脱敏处理器
+ *
+ * @author gaibu
+ */
+public class IdCardDesensitization extends AbstractSliderDesensitizationHandler<IdCardDesensitize> {
+    @Override
+    Integer getPrefixKeep(IdCardDesensitize annotation) {
+        return annotation.prefixKeep();
+    }
+
+    @Override
+    Integer getSuffixKeep(IdCardDesensitize annotation) {
+        return annotation.suffixKeep();
+    }
+
+    @Override
+    String getReplacer(IdCardDesensitize annotation) {
+        return annotation.replacer();
+    }
+}

+ 26 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/MobileDesensitization.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.MobileDesensitize;
+
+/**
+ * {@link MobileDesensitize} 的脱敏处理器
+ *
+ * @author gaibu
+ */
+public class MobileDesensitization extends AbstractSliderDesensitizationHandler<MobileDesensitize> {
+
+    @Override
+    Integer getPrefixKeep(MobileDesensitize annotation) {
+        return annotation.prefixKeep();
+    }
+
+    @Override
+    Integer getSuffixKeep(MobileDesensitize annotation) {
+        return annotation.suffixKeep();
+    }
+
+    @Override
+    String getReplacer(MobileDesensitize annotation) {
+        return annotation.replacer();
+    }
+}

+ 25 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/PasswordDesensitization.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.PasswordDesensitize;
+
+/**
+ * {@link PasswordDesensitize} 的码脱敏处理器
+ *
+ * @author gaibu
+ */
+public class PasswordDesensitization extends AbstractSliderDesensitizationHandler<PasswordDesensitize> {
+    @Override
+    Integer getPrefixKeep(PasswordDesensitize annotation) {
+        return annotation.prefixKeep();
+    }
+
+    @Override
+    Integer getSuffixKeep(PasswordDesensitize annotation) {
+        return annotation.suffixKeep();
+    }
+
+    @Override
+    String getReplacer(PasswordDesensitize annotation) {
+        return annotation.replacer();
+    }
+}

+ 98 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/DesensitizeTest.java

@@ -0,0 +1,98 @@
+package cn.iocoder.yudao.framework.desensitize.core;
+
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.EmailDesensitize;
+import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.RegexDesensitize;
+import cn.iocoder.yudao.framework.desensitize.core.annotation.Address;
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.BankCardDesensitize;
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.CarLicenseDesensitize;
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.ChineseNameDesensitize;
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.FixedPhoneDesensitize;
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.IdCardDesensitize;
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.PasswordDesensitize;
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.MobileDesensitize;
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.SliderDesensitize;
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import lombok.Data;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link DesensitizeTest} 的单元测试
+ */
+public class DesensitizeTest extends BaseMockitoUnitTest {
+
+    @Test
+    public void test() {
+        // 准备参数
+        DesensitizeDemo desensitizeDemo = new DesensitizeDemo();
+        desensitizeDemo.setNickname("芋道源码");
+        desensitizeDemo.setBankCard("9988002866797031");
+        desensitizeDemo.setCarLicense("粤A66666");
+        desensitizeDemo.setFixedPhone("01086551122");
+        desensitizeDemo.setIdCard("530321199204074611");
+        desensitizeDemo.setPassword("123456");
+        desensitizeDemo.setPhoneNumber("13248765917");
+        desensitizeDemo.setSlider1("ABCDEFG");
+        desensitizeDemo.setSlider2("ABCDEFG");
+        desensitizeDemo.setSlider3("ABCDEFG");
+        desensitizeDemo.setEmail("1@email.com");
+        desensitizeDemo.setRegex("你好,我是芋道源码");
+        desensitizeDemo.setAddress("北京市海淀区上地十街10号");
+        desensitizeDemo.setOrigin("芋道源码");
+
+        // 调用
+        DesensitizeDemo d = JsonUtils.parseObject(JsonUtils.toJsonString(desensitizeDemo), DesensitizeDemo.class);
+        // 断言
+        assertNotNull(d);
+        assertEquals("芋***", d.getNickname());
+        assertEquals("998800********31", d.getBankCard());
+        assertEquals("粤A6***6", d.getCarLicense());
+        assertEquals("0108*****22", d.getFixedPhone());
+        assertEquals("530321**********11", d.getIdCard());
+        assertEquals("******", d.getPassword());
+        assertEquals("132****5917", d.getPhoneNumber());
+        assertEquals("#######", d.getSlider1());
+        assertEquals("ABC*EFG", d.getSlider2());
+        assertEquals("*******", d.getSlider3());
+        assertEquals("1****@email.com", d.getEmail());
+        assertEquals("你好,我是*", d.getRegex());
+        assertEquals("北京市海淀区上地十街10号*", d.getAddress());
+        assertEquals("芋道源码", d.getOrigin());
+    }
+
+    @Data
+    public static class DesensitizeDemo {
+
+        @ChineseNameDesensitize
+        private String nickname;
+        @BankCardDesensitize
+        private String bankCard;
+        @CarLicenseDesensitize
+        private String carLicense;
+        @FixedPhoneDesensitize
+        private String fixedPhone;
+        @IdCardDesensitize
+        private String idCard;
+        @PasswordDesensitize
+        private String password;
+        @MobileDesensitize
+        private String phoneNumber;
+        @SliderDesensitize(prefixKeep = 6, suffixKeep = 1, replacer = "#")
+        private String slider1;
+        @SliderDesensitize(prefixKeep = 3, suffixKeep = 3)
+        private String slider2;
+        @SliderDesensitize(prefixKeep = 10)
+        private String slider3;
+        @EmailDesensitize
+        private String email;
+        @RegexDesensitize(regex = "芋道源码", replacer = "*")
+        private String regex;
+        @Address
+        private String address;
+        private String origin;
+
+    }
+
+}

+ 30 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/annotation/Address.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.framework.desensitize.core.annotation;
+
+import cn.iocoder.yudao.framework.desensitize.core.DesensitizeTest;
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
+import cn.iocoder.yudao.framework.desensitize.core.handler.AddressHandler;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 地址
+ *
+ * 用于 {@link DesensitizeTest} 测试使用
+ *
+ * @author gaibu
+ */
+@Documented
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@JacksonAnnotationsInside
+@DesensitizeBy(handler = AddressHandler.class)
+public @interface Address {
+
+    String replacer() default "*";
+
+}

+ 19 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/handler/AddressHandler.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.framework.desensitize.core.handler;
+
+import cn.iocoder.yudao.framework.desensitize.core.DesensitizeTest;
+import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
+import cn.iocoder.yudao.framework.desensitize.core.annotation.Address;
+
+/**
+ * {@link Address} 的脱敏处理器
+ *
+ * 用于 {@link DesensitizeTest} 测试使用
+ */
+public class AddressHandler implements DesensitizationHandler<Address> {
+
+    @Override
+    public String desensitize(String origin, Address annotation) {
+        return origin + annotation.replacer();
+    }
+
+}

+ 7 - 1
yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java

@@ -9,10 +9,11 @@ import io.minio.*;
 import java.io.ByteArrayInputStream;
 
 import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_ALIYUN;
+import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_TENCENT;
 
 /**
  * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
- *
+ * <p>
  * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库
  *
  * @author 芋道源码
@@ -78,6 +79,11 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
                     .replaceAll("-internal", "")// 去除内网 Endpoint 的后缀
                     .replaceAll("https://", "");
         }
+        // 腾讯云必须有 region,否则会报错
+        if (config.getEndpoint().contains(ENDPOINT_TENCENT)) {
+            return StrUtil.subAfter(config.getEndpoint(), ".cos.", false)
+                    .replaceAll("." + ENDPOINT_TENCENT, ""); // 去除 Endpoint
+        }
         return null;
     }
 

+ 1 - 0
yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java

@@ -19,6 +19,7 @@ public class S3FileClientConfig implements FileClientConfig {
 
     public static final String ENDPOINT_QINIU = "qiniucs.com";
     public static final String ENDPOINT_ALIYUN = "aliyuncs.com";
+    public static final String ENDPOINT_TENCENT = "myqcloud.com";
 
     /**
      * 节点地址

+ 17 - 6
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.mybatis.core.mapper;
 
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -10,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.baomidou.mybatisplus.extension.toolkit.Db;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.Collection;
@@ -81,19 +81,30 @@ public interface BaseMapperX<T> extends BaseMapper<T> {
     }
 
     /**
-     * 逐条插入,适合少量数据插入,或者对性能要求不高的场景
-     * <p>
-     * 如果大量,请使用 {@link com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#saveBatch(Collection)} 方法
-     * 使用示例,可见 RoleMenuBatchInsertMapper、UserRoleBatchInsertMapper 类
+     * 批量插入,适合大量数据插入
      *
      * @param entities 实体们
      */
     default void insertBatch(Collection<T> entities) {
-        entities.forEach(this::insert);
+        Db.saveBatch(entities);
+    }
+
+    /**
+     * 批量插入,适合大量数据插入
+     *
+     * @param entities 实体们
+     * @param size     插入数量 Db.saveBatch 默认为 1000
+     */
+    default void insertBatch(Collection<T> entities, int size) {
+        Db.saveBatch(entities, size);
     }
 
     default void updateBatch(T update) {
         update(update, new QueryWrapper<>());
     }
 
+    default void updateBatch(Collection<T> entities, int size) {
+        Db.updateBatchById(entities, size);
+    }
+
 }

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java

@@ -1,7 +1,9 @@
 package cn.iocoder.yudao.framework.mybatis.core.query;
 
+import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.ArrayUtil;
 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
+import cn.iocoder.yudao.framework.mybatis.core.enums.SqlConstants;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;

+ 4 - 4
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java

@@ -146,19 +146,19 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
      *
      * @return this
      */
-    public QueryWrapperX<T> limit1() {
+    public QueryWrapperX<T> limitN(int n) {
         Assert.notNull(SqlConstants.DB_TYPE, "获取不到数据库的类型");
         switch (SqlConstants.DB_TYPE) {
             case ORACLE:
             case ORACLE_12C:
-                super.eq("ROWNUM", 1);
+                super.eq("ROWNUM", n);
                 break;
             case SQL_SERVER:
             case SQL_SERVER2005:
-                super.select("TOP 1 *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条,所以只好使用 * 查询剩余字段
+                super.select("TOP " + n + " *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条,所以只好使用 * 查询剩余字段
                 break;
             default:
-                super.last("LIMIT 1");
+                super.last("LIMIT " + n);
         }
         return this;
     }

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

@@ -129,6 +129,8 @@ public class YudaoWebSecurityConfigurerAdapter {
                 .antMatchers(buildAppApi("/**")).permitAll()
                 // 1.5 验证码captcha 允许匿名访问
                 .antMatchers("/captcha/get", "/captcha/check").permitAll()
+                // 1.6 webSocket 允许匿名访问
+                .antMatchers("/websocket/message").permitAll()
                 // ②:每个项目的自定义规则
                 .and().authorizeRequests(registry -> // 下面,循环设置自定义规则
                         authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(registry)))

+ 8 - 1
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java

@@ -7,7 +7,10 @@ import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import uk.co.jemos.podam.api.PodamFactory;
 import uk.co.jemos.podam.api.PodamFactoryImpl;
+import uk.co.jemos.podam.common.AttributeStrategy;
 
+import javax.validation.constraints.Email;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.time.LocalDateTime;
 import java.util.Arrays;
@@ -47,7 +50,7 @@ public class RandomUtils {
             }
             // 如果是 type、status 结尾的字段,返回 tinyint 范围
             if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(),
-                    "type", "status", "category", "scope")) {
+                    "type", "status", "category", "scope", "result")) {
                 return RandomUtil.randomInt(0, TINYINT_MAX + 1);
             }
             return RandomUtil.randomInt();
@@ -95,6 +98,10 @@ public class RandomUtils {
         return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus();
     }
 
+    public static String randomEmail() {
+        return randomString() + "@qq.com";
+    }
+
     @SafeVarargs
     public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) {
         T pojo = PODAM_FACTORY.manufacturePojo(clazz);

+ 13 - 1
yudao-framework/yudao-spring-boot-starter-web/pom.xml

@@ -21,6 +21,12 @@
             <artifactId>yudao-common</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
         <!-- Web 相关 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -35,7 +41,7 @@
 
         <dependency>
             <groupId>com.github.xiaoymin</groupId>
-            <artifactId>knife4j-spring-boot-starter</artifactId>
+            <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
         </dependency>
         <dependency>
             <groupId>io.swagger</groupId>
@@ -67,6 +73,12 @@
             <scope>provided</scope> <!-- 设置为 provided,主要是 GlobalExceptionHandler 使用 -->
         </dependency>
 
+        <!-- xss -->
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>

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

@@ -1 +1,4 @@
+/**
+ * Web 框架,全局异常、API 日志等
+ */
 package cn.iocoder.yudao.framework;

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

@@ -9,15 +9,16 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
 import org.springframework.context.annotation.Bean;
 import org.springframework.http.HttpHeaders;
 import springfox.documentation.builders.ApiInfoBuilder;
-import springfox.documentation.builders.ExampleBuilder;
+import springfox.documentation.builders.ParameterBuilder;
 import springfox.documentation.builders.PathSelectors;
-import springfox.documentation.builders.RequestParameterBuilder;
+import springfox.documentation.schema.ModelRef;
 import springfox.documentation.service.*;
 import springfox.documentation.spi.DocumentationType;
 import springfox.documentation.spi.service.contexts.SecurityContext;
 import springfox.documentation.spring.web.plugins.Docket;
-import springfox.documentation.swagger2.annotations.EnableSwagger2;
+import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -30,7 +31,7 @@ import static springfox.documentation.builders.RequestHandlerSelectors.basePacka
  * @author 芋道源码
  */
 @AutoConfiguration
-@EnableSwagger2
+@EnableSwagger2WebMvc
 @EnableKnife4j
 @ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
 // 允许使用 swagger.enable=false 禁用 Swagger
@@ -59,7 +60,7 @@ public class YudaoSwaggerAutoConfiguration {
                 .securitySchemes(securitySchemes())
                 .securityContexts(securityContexts())
                 // ④ 全局参数(多租户 header)
-                .globalRequestParameters(globalRequestParameters());
+                .globalOperationParameters(globalRequestParameters());
     }
 
     // ========== apiInfo ==========
@@ -95,7 +96,7 @@ public class YudaoSwaggerAutoConfiguration {
         return Collections.singletonList(SecurityContext.builder()
                 .securityReferences(securityReferences())
                 // 通过 PathSelectors.regex("^(?!auth).*$"),排除包含 "auth" 的接口不需要使用securitySchemes
-                .operationSelector(o -> o.requestMappingPattern().matches("^(?!auth).*$"))
+                .forPaths(PathSelectors.regex("^(?!auth).*$"))
                 .build());
     }
 
@@ -109,11 +110,17 @@ public class YudaoSwaggerAutoConfiguration {
 
     // ========== globalRequestParameters ==========
 
-    private static List<RequestParameter> globalRequestParameters() {
-        RequestParameterBuilder tenantParameter = new RequestParameterBuilder()
-                .name(HEADER_TENANT_ID).description("租户编号")
-                .in(ParameterType.HEADER).example(new ExampleBuilder().value(1L).build());
-        return Collections.singletonList(tenantParameter.build());
+    private static List<Parameter> globalRequestParameters() {
+        List<Parameter> tenantParameter = new ArrayList<>();
+        tenantParameter.add(new ParameterBuilder()
+                .name(HEADER_TENANT_ID)
+                .description("租户编号")
+                .modelRef(new ModelRef("long"))
+                .defaultValue("1")
+                .parameterType("header")
+                .required(true)
+                .build());
+        return tenantParameter;
     }
 
 }

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

@@ -5,7 +5,6 @@ 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;
@@ -20,7 +19,7 @@ public class SpringFoxHandlerProviderBeanPostProcessor implements BeanPostProces
 
     @Override
     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
-        if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
+        if (bean instanceof WebMvcRequestHandlerProvider) {
             customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
         }
         return bean;

+ 3 - 13
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java

@@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkServic
 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
 import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter;
 import cn.iocoder.yudao.framework.web.core.filter.DemoFilter;
-import cn.iocoder.yudao.framework.web.core.filter.XssFilter;
 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
 import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
@@ -15,7 +14,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.util.AntPathMatcher;
-import org.springframework.util.PathMatcher;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@@ -27,7 +25,7 @@ import javax.annotation.Resource;
 import javax.servlet.Filter;
 
 @AutoConfiguration
-@EnableConfigurationProperties({WebProperties.class, XssProperties.class})
+@EnableConfigurationProperties(WebProperties.class)
 public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
 
     @Resource
@@ -48,7 +46,7 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
      * 设置 API 前缀,仅仅匹配 controller 包下的
      *
      * @param configurer 配置
-     * @param api API 配置
+     * @param api        API 配置
      */
     private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) {
         AntPathMatcher antPathMatcher = new AntPathMatcher(".");
@@ -100,14 +98,6 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
         return createFilterBean(new CacheRequestBodyFilter(), WebFilterOrderEnum.REQUEST_BODY_CACHE_FILTER);
     }
 
-    /**
-     * 创建 XssFilter Bean,解决 Xss 安全问题
-     */
-    @Bean
-    public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher) {
-        return createFilterBean(new XssFilter(properties, pathMatcher), WebFilterOrderEnum.XSS_FILTER);
-    }
-
     /**
      * 创建 DemoFilter Bean,演示模式
      */
@@ -117,7 +107,7 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
         return createFilterBean(new DemoFilter(), WebFilterOrderEnum.DEMO_FILTER);
     }
 
-    private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
+    public static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
         FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
         bean.setOrder(order);
         return bean;

+ 0 - 136
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/XssRequestWrapper.java

@@ -1,136 +0,0 @@
-package cn.iocoder.yudao.framework.web.core.filter;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.ReflectUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.http.HTMLFilter;
-import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
-
-import javax.servlet.ReadListener;
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.Map;
-
-/**
- * Xss 请求 Wrapper
- *
- * @author 芋道源码
- */
-public class XssRequestWrapper extends HttpServletRequestWrapper {
-
-    /**
-     * 基于线程级别的 HTMLFilter 对象,因为它线程非安全
-     */
-    private static final ThreadLocal<HTMLFilter> HTML_FILTER = ThreadLocal.withInitial(() -> {
-        HTMLFilter htmlFilter = new HTMLFilter();
-        // 反射修改 encodeQuotes 属性为 false,避免 " 被转移成 &quot; 字符
-        ReflectUtil.setFieldValue(htmlFilter, "encodeQuotes", false);
-        return htmlFilter;
-    });
-
-    public XssRequestWrapper(HttpServletRequest request) {
-        super(request);
-    }
-
-    private static String filterXss(String content) {
-        if (StrUtil.isEmpty(content)) {
-            return content;
-        }
-        return HTML_FILTER.get().filter(content);
-    }
-
-    // ========== IO 流相关 ==========
-
-    @Override
-    public BufferedReader getReader() throws IOException {
-        return new BufferedReader(new InputStreamReader(this.getInputStream()));
-    }
-
-    @Override
-    public ServletInputStream getInputStream() throws IOException {
-        // 如果非 json 请求,不进行 Xss 处理
-        if (!ServletUtils.isJsonRequest(this)) {
-            return super.getInputStream();
-        }
-
-        // 读取内容,并过滤
-        String content = IoUtil.readUtf8(super.getInputStream());
-        content = filterXss(content);
-        final ByteArrayInputStream newInputStream = new ByteArrayInputStream(content.getBytes());
-        // 返回 ServletInputStream
-        return new ServletInputStream() {
-
-            @Override
-            public int read() {
-                return newInputStream.read();
-            }
-
-            @Override
-            public boolean isFinished() {
-                return true;
-            }
-
-            @Override
-            public boolean isReady() {
-                return true;
-            }
-
-            @Override
-            public void setReadListener(ReadListener readListener) {}
-
-        };
-    }
-
-    // ========== Param 相关 ==========
-
-    @Override
-    public String getParameter(String name) {
-        String value = super.getParameter(name);
-        return filterXss(value);
-    }
-
-    @Override
-    public String[] getParameterValues(String name) {
-        String[] values = super.getParameterValues(name);
-        if (ArrayUtil.isEmpty(values)) {
-            return values;
-        }
-        // 过滤处理
-        for (int i = 0; i < values.length; i++) {
-            values[i] = filterXss(values[i]);
-        }
-        return values;
-    }
-
-    @Override
-    public Map<String, String[]> getParameterMap() {
-        Map<String, String[]> valueMap = super.getParameterMap();
-        if (CollUtil.isEmpty(valueMap)) {
-            return valueMap;
-        }
-        // 过滤处理
-        for (Map.Entry<String, String[]> entry : valueMap.entrySet()) {
-            String[] values = entry.getValue();
-            for (int i = 0; i < values.length; i++) {
-                values[i] = filterXss(values[i]);
-            }
-        }
-        return valueMap;
-    }
-
-    // ========== Header 相关 ==========
-
-    @Override
-    public String getHeader(String name) {
-        String value = super.getHeader(name);
-        return filterXss(value);
-    }
-
-}

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/XssProperties.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/XssProperties.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.web.config;
+package cn.iocoder.yudao.framework.xss.config;
 
 import lombok.Data;
 import org.springframework.boot.context.properties.ConfigurationProperties;

+ 60 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java

@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.framework.xss.config;
+
+import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
+import cn.iocoder.yudao.framework.xss.core.clean.JsoupXssCleaner;
+import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
+import cn.iocoder.yudao.framework.xss.core.filter.XssFilter;
+import cn.iocoder.yudao.framework.xss.core.json.XssStringJsonDeserializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.util.PathMatcher;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import static cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration.createFilterBean;
+
+@AutoConfiguration
+@EnableConfigurationProperties(XssProperties.class)
+public class YudaoXssAutoConfiguration implements WebMvcConfigurer {
+
+    /**
+     * Xss 清理者
+     *
+     * @return XssCleaner
+     */
+    @Bean
+    @ConditionalOnMissingBean(XssCleaner.class)
+    public XssCleaner xssCleaner() {
+        return new JsoupXssCleaner();
+    }
+
+    /**
+     * 注册 Jackson 的序列化器,用于处理 json 类型参数的 xss 过滤
+     *
+     * @return Jackson2ObjectMapperBuilderCustomizer
+     */
+    @Bean
+    @ConditionalOnMissingBean(name = "xssJacksonCustomizer")
+    @ConditionalOnBean(ObjectMapper.class)
+    @ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true")
+    public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssCleaner xssCleaner) {
+        // 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理
+        return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(xssCleaner));
+    }
+
+    /**
+     * 创建 XssFilter Bean,解决 Xss 安全问题
+     */
+    @Bean
+    @ConditionalOnBean(XssCleaner.class)
+    public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher, XssCleaner xssCleaner) {
+        return createFilterBean(new XssFilter(properties, pathMatcher, xssCleaner), WebFilterOrderEnum.XSS_FILTER);
+    }
+
+}

+ 64 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/clean/JsoupXssCleaner.java

@@ -0,0 +1,64 @@
+package cn.iocoder.yudao.framework.xss.core.clean;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.safety.Safelist;
+
+/**
+ * 基于 JSONP 实现 XSS 过滤字符串
+ */
+public class JsoupXssCleaner implements XssCleaner {
+
+    private final Safelist safelist;
+
+    /**
+     * 用于在 src 属性使用相对路径时,强制转换为绝对路径。 为空时不处理,值应为绝对路径的前缀(包含协议部分)
+     */
+    private final String baseUri;
+
+    /**
+     * 无参构造,默认使用 {@link JsoupXssCleaner#buildSafelist} 方法构建一个安全列表
+     */
+    public JsoupXssCleaner() {
+        this.safelist = buildSafelist();
+        this.baseUri = "";
+    }
+
+    /**
+     * 构建一个 Xss 清理的 Safelist 规则。
+     * 基于 Safelist#relaxed() 的基础上:
+     * 1. 扩展支持了 style 和 class 属性
+     * 2. a 标签额外支持了 target 属性
+     * 3. img 标签额外支持了 data 协议,便于支持 base64
+     *
+     * @return Safelist
+     */
+    private Safelist buildSafelist() {
+        // 使用 jsoup 提供的默认的
+        Safelist relaxedSafelist = Safelist.relaxed();
+        // 富文本编辑时一些样式是使用 style 来进行实现的
+        // 比如红色字体 style="color:red;", 所以需要给所有标签添加 style 属性
+        // 注意:style 属性会有注入风险 <img STYLE="background-image:url(javascript:alert('XSS'))">
+        relaxedSafelist.addAttributes(":all", "style", "class");
+        // 保留 a 标签的 target 属性
+        relaxedSafelist.addAttributes("a", "target");
+        // 支持img 为base64
+        relaxedSafelist.addProtocols("img", "src", "data");
+
+        // 保留相对路径, 保留相对路径时,必须提供对应的 baseUri 属性,否则依然会被删除
+        // WHITELIST.preserveRelativeLinks(false);
+
+        // 移除 a 标签和 img 标签的一些协议限制,这会导致 xss 防注入失效,如 <img src=javascript:alert("xss")>
+        // 虽然可以重写 WhiteList#isSafeAttribute 来处理,但是有隐患,所以暂时不支持相对路径
+        // WHITELIST.removeProtocols("a", "href", "ftp", "http", "https", "mailto");
+        // WHITELIST.removeProtocols("img", "src", "http", "https");
+        return relaxedSafelist;
+    }
+
+    @Override
+    public String clean(String html) {
+        return Jsoup.clean(html, baseUri, safelist, new Document.OutputSettings().prettyPrint(false));
+    }
+
+}
+

+ 16 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/clean/XssCleaner.java

@@ -0,0 +1,16 @@
+package cn.iocoder.yudao.framework.xss.core.clean;
+
+/**
+ * 对 html 文本中的有 Xss 风险的数据进行清理
+ */
+public interface XssCleaner {
+
+    /**
+     * 清理有 Xss 风险的文本
+     *
+     * @param html 原 html
+     * @return 清理后的 html
+     */
+    String clean(String html);
+
+}

+ 6 - 5
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/XssFilter.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/filter/XssFilter.java

@@ -1,6 +1,7 @@
-package cn.iocoder.yudao.framework.web.core.filter;
+package cn.iocoder.yudao.framework.xss.core.filter;
 
-import cn.iocoder.yudao.framework.web.config.XssProperties;
+import cn.iocoder.yudao.framework.xss.config.XssProperties;
+import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
 import lombok.AllArgsConstructor;
 import org.springframework.util.PathMatcher;
 import org.springframework.web.filter.OncePerRequestFilter;
@@ -14,8 +15,6 @@ import java.io.IOException;
 /**
  * Xss 过滤器
  *
- * 对 Xss 不了解的胖友,可以看看 http://www.iocoder.cn/Fight/The-new-girl-asked-me-why-AJAX-requests-are-not-secure-I-did-not-answer/
- *
  * @author 芋道源码
  */
 @AllArgsConstructor
@@ -30,10 +29,12 @@ public class XssFilter extends OncePerRequestFilter {
      */
     private final PathMatcher pathMatcher;
 
+    private final XssCleaner xssCleaner;
+
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
             throws IOException, ServletException {
-        filterChain.doFilter(new XssRequestWrapper(request), response);
+        filterChain.doFilter(new XssRequestWrapper(request, xssCleaner), response);
     }
 
     @Override

+ 92 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/filter/XssRequestWrapper.java

@@ -0,0 +1,92 @@
+package cn.iocoder.yudao.framework.xss.core.filter;
+
+import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Xss 请求 Wrapper
+ *
+ * @author 芋道源码
+ */
+public class XssRequestWrapper extends HttpServletRequestWrapper {
+
+    private final XssCleaner xssCleaner;
+
+    public XssRequestWrapper(HttpServletRequest request, XssCleaner xssCleaner) {
+        super(request);
+        this.xssCleaner = xssCleaner;
+    }
+
+    // ============================ parameter ============================
+    @Override
+    public Map<String, String[]> getParameterMap() {
+        Map<String, String[]> map = new LinkedHashMap<>();
+        Map<String, String[]> parameters = super.getParameterMap();
+        for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
+            String[] values = entry.getValue();
+            for (int i = 0; i < values.length; i++) {
+                values[i] = xssCleaner.clean(values[i]);
+            }
+            map.put(entry.getKey(), values);
+        }
+        return map;
+    }
+
+    @Override
+    public String[] getParameterValues(String name) {
+        String[] values = super.getParameterValues(name);
+        if (values == null) {
+            return null;
+        }
+        int count = values.length;
+        String[] encodedValues = new String[count];
+        for (int i = 0; i < count; i++) {
+            encodedValues[i] = xssCleaner.clean(values[i]);
+        }
+        return encodedValues;
+    }
+
+    @Override
+    public String getParameter(String name) {
+        String value = super.getParameter(name);
+        if (value == null) {
+            return null;
+        }
+        return xssCleaner.clean(value);
+    }
+
+    // ============================ attribute ============================
+    @Override
+    public Object getAttribute(String name) {
+        Object value = super.getAttribute(name);
+        if (value instanceof String) {
+            xssCleaner.clean((String) value);
+        }
+        return value;
+    }
+
+    // ============================ header ============================
+    @Override
+    public String getHeader(String name) {
+        String value = super.getHeader(name);
+        if (value == null) {
+            return null;
+        }
+        return xssCleaner.clean(value);
+    }
+
+    // ============================ queryString ============================
+    @Override
+    public String getQueryString() {
+        String value = super.getQueryString();
+        if (value == null) {
+            return null;
+        }
+        return xssCleaner.clean(value);
+    }
+
+}

+ 59 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/json/XssStringJsonDeserializer.java

@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.framework.xss.core.json;
+
+import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+
+/**
+ * XSS 过滤 jackson 反序列化器。
+ * 在反序列化的过程中,会对字符串进行 XSS 过滤。
+ *
+ * @author Hccake
+ */
+@Slf4j
+@AllArgsConstructor
+public class XssStringJsonDeserializer extends StringDeserializer {
+
+    private final XssCleaner xssCleaner;
+
+    @Override
+    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+        if (p.hasToken(JsonToken.VALUE_STRING)) {
+            return xssCleaner.clean(p.getText());
+        }
+        JsonToken t = p.currentToken();
+        // [databind#381]
+        if (t == JsonToken.START_ARRAY) {
+            return _deserializeFromArray(p, ctxt);
+        }
+        // need to gracefully handle byte[] data, as base64
+        if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
+            Object ob = p.getEmbeddedObject();
+            if (ob == null) {
+                return null;
+            }
+            if (ob instanceof byte[]) {
+                return ctxt.getBase64Variant().encode((byte[]) ob, false);
+            }
+            // otherwise, try conversion using toString()...
+            return ob.toString();
+        }
+        // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
+        if (t == JsonToken.START_OBJECT) {
+            return ctxt.extractScalarFromObject(p, this, _valueClass);
+        }
+
+        if (t.isScalarValue()) {
+            String text = p.getValueAsString();
+            return xssCleaner.clean(text);
+        }
+        return (String) ctxt.handleUnexpectedToken(_valueClass, p);
+    }
+}
+

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

@@ -0,0 +1,6 @@
+/**
+ * 针对 XSS 的基础封装
+ *
+ * XSS 说明:https://tech.meituan.com/2018/09/27/fe-security.html
+ */
+package cn.iocoder.yudao.framework.xss;

+ 2 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1,4 +1,5 @@
 cn.iocoder.yudao.framework.apilog.config.YudaoApiLogAutoConfiguration
 cn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration
 cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration
-cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration
+cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration
+cn.iocoder.yudao.framework.xss.config.YudaoXssAutoConfiguration

+ 37 - 0
yudao-framework/yudao-spring-boot-starter-websocket/pom.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao-framework</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-spring-boot-starter-websocket</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>WebSocket</description>
+    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
+
+
+    <dependencies>
+
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 14 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketHandlerConfig.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.framework.websocket.config;
+
+import cn.iocoder.yudao.framework.websocket.core.UserHandshakeInterceptor;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+@EnableConfigurationProperties(WebSocketProperties.class)
+public class WebSocketHandlerConfig {
+    @Bean
+    public HandshakeInterceptor handshakeInterceptor() {
+        return new UserHandshakeInterceptor();
+    }
+}

+ 29 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/WebSocketProperties.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.framework.websocket.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * WebSocket 配置项
+ *
+ * @author xingyu4j
+ */
+@ConfigurationProperties("yudao.websocket")
+@Data
+@Validated
+public class WebSocketProperties {
+
+    /**
+     * 路径
+     */
+    private String path = "";
+    /**
+     * 默认最多允许同时在线用户数
+     */
+    private int maxOnlineCount = 0;
+    /**
+     * 是否保存session
+     */
+    private boolean sessionMap = true;
+}

+ 34 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/config/YudaoWebSocketAutoConfiguration.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.framework.websocket.config;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+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;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+import java.util.List;
+
+/**
+ * WebSocket 自动配置
+ *
+ * @author xingyu4j
+ */
+@AutoConfiguration
+// 允许使用 yudao.websocket.enable=false 禁用websocket
+@ConditionalOnProperty(prefix = "yudao.websocket", value = "enable", matchIfMissing = true)
+@EnableConfigurationProperties(WebSocketProperties.class)
+public class YudaoWebSocketAutoConfiguration {
+    @Bean
+    @ConditionalOnMissingBean
+    public WebSocketConfigurer webSocketConfigurer(List<HandshakeInterceptor> handshakeInterceptor,
+                                                   WebSocketHandler webSocketHandler,
+                                                   WebSocketProperties webSocketProperties) {
+
+        return registry -> registry
+                .addHandler(webSocketHandler, webSocketProperties.getPath())
+                .addInterceptors(handshakeInterceptor.toArray(new HandshakeInterceptor[0]));
+    }
+}

+ 24 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/UserHandshakeInterceptor.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.framework.websocket.core;
+
+import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+import java.util.Map;
+
+public class UserHandshakeInterceptor implements HandshakeInterceptor {
+    @Override
+    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
+        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
+        attributes.put(WebSocketKeyDefine.LOGIN_USER, loginUser);
+        return true;
+    }
+
+    @Override
+    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
+
+    }
+}

+ 9 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketKeyDefine.java

@@ -0,0 +1,9 @@
+package cn.iocoder.yudao.framework.websocket.core;
+
+
+import lombok.Data;
+
+@Data
+public class WebSocketKeyDefine {
+    public static final String LOGIN_USER ="LOGIN_USER";
+}

+ 24 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketMessageDO.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.framework.websocket.core;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+public class WebSocketMessageDO {
+    /**
+     * 接收消息的seesion
+     */
+    private List<Object> seesionKeyList;
+    /**
+     * 发送消息
+     */
+    private String msgText;
+
+    public static WebSocketMessageDO build(List<Object> seesionKeyList, String msgText) {
+        return new WebSocketMessageDO().setMsgText(msgText).setSeesionKeyList(seesionKeyList);
+    }
+
+}

+ 36 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketSessionHandler.java

@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.framework.websocket.core;
+
+import org.springframework.web.socket.WebSocketSession;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public final class WebSocketSessionHandler {
+    private WebSocketSessionHandler() {
+    }
+
+    private static final Map<String, WebSocketSession> SESSION_MAP = new ConcurrentHashMap<>();
+
+    public static void addSession(Object sessionKey, WebSocketSession session) {
+        SESSION_MAP.put(sessionKey.toString(), session);
+    }
+
+    public static void removeSession(Object sessionKey) {
+        SESSION_MAP.remove(sessionKey.toString());
+    }
+
+    public static WebSocketSession getSession(Object sessionKey) {
+        return SESSION_MAP.get(sessionKey.toString());
+    }
+
+    public static Collection<WebSocketSession> getSessions() {
+        return SESSION_MAP.values();
+    }
+
+    public static Set<String> getSessionKeys() {
+        return SESSION_MAP.keySet();
+    }
+
+}

+ 31 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/WebSocketUtils.java

@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.framework.websocket.core;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.io.IOException;
+
+@Slf4j
+public class WebSocketUtils {
+    public static boolean sendMessage(WebSocketSession seesion, String message) {
+        if (seesion == null) {
+            log.error("seesion 不存在");
+            return false;
+        }
+        if (seesion.isOpen()) {
+            try {
+                seesion.sendMessage(new TextMessage(message));
+            } catch (IOException e) {
+                log.error("WebSocket 消息发送异常 Session={} | msg= {} | exception={}", seesion, message, e);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static boolean sendMessage(Object sessionKey, String message) {
+        WebSocketSession session = WebSocketSessionHandler.getSession(sessionKey);
+        return sendMessage(session, message);
+    }
+}

+ 49 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/YudaoWebSocketHandlerDecorator.java

@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.framework.websocket.core;
+
+import cn.iocoder.yudao.framework.security.core.LoginUser;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
+
+public class YudaoWebSocketHandlerDecorator extends WebSocketHandlerDecorator {
+    public YudaoWebSocketHandlerDecorator(WebSocketHandler delegate) {
+        super(delegate);
+    }
+
+    /**
+     * websocket 连接时执行的动作
+     * @param session websocket session 对象
+     * @throws Exception 异常对象
+     */
+    @Override
+    public void afterConnectionEstablished(final WebSocketSession session) throws Exception {
+        Object sessionKey = sessionKeyGen(session);
+        WebSocketSessionHandler.addSession(sessionKey, session);
+    }
+
+    /**
+     * websocket 关闭连接时执行的动作
+     * @param session websocket session 对象
+     * @param closeStatus 关闭状态对象
+     * @throws Exception 异常对象
+     */
+    @Override
+    public void afterConnectionClosed(final WebSocketSession session, CloseStatus closeStatus) throws Exception {
+        Object sessionKey = sessionKeyGen(session);
+        WebSocketSessionHandler.removeSession(sessionKey);
+    }
+
+    public Object sessionKeyGen(WebSocketSession webSocketSession) {
+
+        Object obj = webSocketSession.getAttributes().get(WebSocketKeyDefine.LOGIN_USER);
+
+        if (obj instanceof LoginUser) {
+            LoginUser loginUser = (LoginUser) obj;
+            // userId 作为唯一区分
+            return String.valueOf(loginUser.getId());
+        }
+
+        return null;
+    }
+}

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

@@ -0,0 +1 @@
+package cn.iocoder.yudao.framework.websocket;

+ 1 - 0
yudao-framework/yudao-spring-boot-starter-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -0,0 +1 @@
+cn.iocoder.yudao.framework.websocket.config.YudaoWebSocketAutoConfiguration

+ 7 - 7
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java

@@ -213,18 +213,18 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
 
     private void validTaskAssignRuleOptions(Integer type, Set<Long> options) {
         if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) {
-            roleApi.validRoles(options);
+            roleApi.validRoleList(options);
         } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
             BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
-            deptApi.validDepts(options);
+            deptApi.validateDeptList(options);
         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) {
-            postApi.validPosts(options);
+            postApi.validPostList(options);
         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER.getType())) {
-            adminUserApi.validUsers(options);
+            adminUserApi.validateUserList(options);
         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_GROUP.getType())) {
             userGroupService.validUserGroups(options);
         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) {
-            dictDataApi.validDictDatas(DictTypeConstants.TASK_ASSIGN_SCRIPT,
+            dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
                 CollectionUtils.convertSet(options, String::valueOf));
         } else {
             throw new IllegalArgumentException(format("未知的规则类型({})", type));
@@ -288,12 +288,12 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
     }
 
     private Set<Long> calculateTaskCandidateUsersByDeptMember(BpmTaskAssignRuleDO rule) {
-        List<AdminUserRespDTO> users = adminUserApi.getUsersByDeptIds(rule.getOptions());
+        List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(rule.getOptions());
         return convertSet(users, AdminUserRespDTO::getId);
     }
 
     private Set<Long> calculateTaskCandidateUsersByDeptLeader(BpmTaskAssignRuleDO rule) {
-        List<DeptRespDTO> depts = deptApi.getDepts(rule.getOptions());
+        List<DeptRespDTO> depts = deptApi.getDeptList(rule.getOptions());
         return convertSet(depts, DeptRespDTO::getLeaderUserId);
     }
 

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImplTest.java

@@ -86,7 +86,7 @@ public class BpmTaskAssignRuleServiceImplTest extends BaseDbUnitTest {
         // mock 方法
         List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
                 id -> new AdminUserRespDTO().setId(id));
-        when(adminUserApi.getUsersByDeptIds(eq(rule.getOptions()))).thenReturn(users);
+        when(adminUserApi.getUserListByDeptIds(eq(rule.getOptions()))).thenReturn(users);
         mockGetUserMap(asSet(11L, 22L));
 
         // 调用

+ 6 - 0
yudao-module-infra/yudao-module-infra-biz/pom.xml

@@ -111,6 +111,12 @@
             <groupId>cn.iocoder.boot</groupId>
             <artifactId>yudao-spring-boot-starter-file</artifactId>
         </dependency>
+
+        <!-- WebSocket -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 6 - 6
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.infra.controller.admin.config;
 
-import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
@@ -8,8 +7,8 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
 import cn.iocoder.yudao.module.infra.controller.admin.config.vo.*;
 import cn.iocoder.yudao.module.infra.convert.config.ConfigConvert;
 import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
-import cn.iocoder.yudao.module.infra.service.config.ConfigService;
 import cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants;
+import cn.iocoder.yudao.module.infra.service.config.ConfigService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiOperation;
@@ -23,6 +22,7 @@ import javax.validation.Valid;
 import java.io.IOException;
 import java.util.List;
 
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
 
@@ -75,8 +75,8 @@ public class ConfigController {
         if (config == null) {
             return null;
         }
-        if (config.getVisible()) {
-            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_VISIBLE);
+        if (!config.getVisible()) {
+            throw exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_VISIBLE);
         }
         return success(config.getValue());
     }
@@ -93,8 +93,8 @@ public class ConfigController {
     @ApiOperation("导出参数配置")
     @PreAuthorize("@ss.hasPermission('infra:config:export')")
     @OperateLog(type = EXPORT)
-    public void exportSysConfig(@Valid ConfigExportReqVO reqVO,
-                                HttpServletResponse response) throws IOException {
+    public void exportConfig(@Valid ConfigExportReqVO reqVO,
+                             HttpServletResponse response) throws IOException {
         List<ConfigDO> list = configService.getConfigList(reqVO);
         // 拼接数据
         List<ConfigExcelVO> datas = ConfigConvert.INSTANCE.convertList(list);

+ 8 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java

@@ -11,9 +11,11 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
 import com.baomidou.mybatisplus.generator.config.po.TableField;
 import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import org.apache.ibatis.type.JdbcType;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
 import org.mapstruct.Mappings;
+import org.mapstruct.Named;
 import org.mapstruct.factory.Mappers;
 
 import java.util.List;
@@ -37,7 +39,7 @@ public interface CodegenConvert {
 
     @Mappings({
             @Mapping(source = "name", target = "columnName"),
-            @Mapping(source = "type", target = "dataType"),
+            @Mapping(source = "metaInfo.jdbcType", target = "dataType", qualifiedByName = "getDataType"),
             @Mapping(source = "comment", target = "columnComment"),
             @Mapping(source = "metaInfo.nullable", target = "nullable"),
             @Mapping(source = "keyFlag", target = "primaryKey"),
@@ -47,6 +49,11 @@ public interface CodegenConvert {
     })
     CodegenColumnDO convert(TableField bean);
 
+    @Named("getDataType")
+    default String getDataType(JdbcType jdbcType) {
+        return jdbcType.name();
+    }
+
     // ========== CodegenTableDO 相关 ==========
 
 //    List<CodegenTableRespVO> convertList02(List<CodegenTableDO> list);

+ 22 - 5
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenColumnDO.java

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnu
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.generator.config.po.TableField;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
@@ -29,7 +30,7 @@ public class CodegenColumnDO extends BaseDO {
     private Long id;
     /**
      * 表编号
-     *
+     * <p>
      * 关联 {@link CodegenTableDO#getId()}
      */
     private Long tableId;
@@ -38,26 +39,38 @@ public class CodegenColumnDO extends BaseDO {
 
     /**
      * 字段名
+     *
+     * 关联 {@link TableField#getName()}
      */
     private String columnName;
     /**
-     * 字段类型
+     * 数据库字段类型
+     *
+     * 关联 {@link TableField.MetaInfo#getJdbcType()}
      */
     private String dataType;
     /**
      * 字段描述
+     *
+     * 关联 {@link TableField#getComment()}
      */
     private String columnComment;
     /**
      * 是否允许为空
+     *
+     * 关联 {@link TableField.MetaInfo#isNullable()}
      */
     private Boolean nullable;
     /**
      * 是否主键
+     *
+     * 关联 {@link TableField#isKeyFlag()}
      */
     private Boolean primaryKey;
     /**
      * 是否自增
+     *
+     * 关联 {@link TableField#isKeyIdentityFlag()}
      */
     private Boolean autoIncrement;
     /**
@@ -71,15 +84,19 @@ public class CodegenColumnDO extends BaseDO {
      * Java 属性类型
      *
      * 例如说 String、Boolean 等等
+     *
+     * 关联 {@link TableField#getColumnType()}
      */
     private String javaType;
     /**
      * Java 属性名
+     *
+     * 关联 {@link TableField#getPropertyName()}
      */
     private String javaField;
     /**
      * 字典类型
-     *
+     * <p>
      * 关联 DictTypeDO 的 type 属性
      */
     private String dictType;
@@ -104,7 +121,7 @@ public class CodegenColumnDO extends BaseDO {
     private Boolean listOperation;
     /**
      * List 查询操作的条件类型
-     *
+     * <p>
      * 枚举 {@link CodegenColumnListConditionEnum}
      */
     private String listOperationCondition;
@@ -117,7 +134,7 @@ public class CodegenColumnDO extends BaseDO {
 
     /**
      * 显示类型
-     *
+     * <p>
      * 枚举 {@link CodegenColumnHtmlTypeEnum}
      */
     private String htmlType;

+ 5 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
@@ -44,10 +45,14 @@ public class CodegenTableDO extends BaseDO {
 
     /**
      * 表名称
+     *
+     * 关联 {@link TableInfo#getName()}
      */
     private String tableName;
     /**
      * 表描述
+     *
+     * 关联 {@link TableInfo#getComment()}
      */
     private String tableComment;
     /**

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