mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-01-19 19:50:06 +08:00
Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
Conflicts: yudao-ui-admin/yarn.lock
This commit is contained in:
commit
4247061b96
51
README.md
51
README.md
@ -41,7 +41,20 @@
|
|||||||
| `ruoyi-vue-pro` | Spring Boot 多模块 | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-pro)** [Github](https://github.com/YunaiV/ruoyi-vue-pro) |
|
| `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) |
|
| `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) |
|
| `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)** |
|
|
||||||
|
## 🐰 分支说明
|
||||||
|
|
||||||
|
| | 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 行代码注释。
|
③ 代码整洁、架构整洁,遵循《阿里巴巴 Java 开发手册》规范,代码注释详细,57000 行 Java 代码,22000 行代码注释。
|
||||||
|
|
||||||
|
## 🤝 项目外包
|
||||||
|
|
||||||
|
我们也是接外包滴,如果你有项目想要外包,可以微信联系【**Aix9975**】。
|
||||||
|
|
||||||
|
团队包含专业的项目经理、架构师、前端工程师、后端工程师、测试工程师、运维工程师,可以提供全流程的外包服务。
|
||||||
|
|
||||||
|
项目可以是商城、SCRM 系统、OA 系统、物流系统、ERP 系统、CMS 系统、HIS 系统、支付系统、IM 聊天、微信公众号、微信小程序等等。
|
||||||
|
|
||||||
## 🐼 内置功能
|
## 🐼 内置功能
|
||||||
|
|
||||||
系统内置多种多种业务功能,可以用于快速你的业务系统:
|
系统内置多种多种业务功能,可以用于快速你的业务系统:
|
||||||
@ -68,6 +89,7 @@
|
|||||||
* 会员中心
|
* 会员中心
|
||||||
* 数据报表
|
* 数据报表
|
||||||
* 商城系统
|
* 商城系统
|
||||||
|
* 微信公众号
|
||||||
|
|
||||||
> 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
|
> 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
|
||||||
>
|
>
|
||||||
@ -90,6 +112,8 @@
|
|||||||
| 🚀 | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 |
|
| 🚀 | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 |
|
||||||
| | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 |
|
| | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 |
|
||||||
| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
|
| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
|
||||||
|
| 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 |
|
||||||
|
| 🚀 | 站内信 | 系统内的消息通知,提供站内信模版、站内信消息 |
|
||||||
| 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
|
| 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
|
||||||
| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 |
|
| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 |
|
||||||
| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |
|
| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |
|
||||||
@ -152,6 +176,21 @@ ps:核心功能已经实现,正在对接微信小程序中...
|
|||||||
| 🚀 | 报表设计器 | 支持数据报表、图形报表、打印设计等 |
|
| 🚀 | 报表设计器 | 支持数据报表、图形报表、打印设计等 |
|
||||||
| 🚀 | 大屏设计器 | 建设中... 拖拽式实现可视化数据大屏 |
|
| 🚀 | 大屏设计器 | 建设中... 拖拽式实现可视化数据大屏 |
|
||||||
|
|
||||||
|
### 微信公众号
|
||||||
|
|
||||||
|
| | 功能 | 描述 |
|
||||||
|
|-----|--------|-------------------------------|
|
||||||
|
| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 |
|
||||||
|
| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 |
|
||||||
|
| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 |
|
||||||
|
| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 |
|
||||||
|
| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 |
|
||||||
|
| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 |
|
||||||
|
| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 |
|
||||||
|
| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 |
|
||||||
|
| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 |
|
||||||
|
| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 |
|
||||||
|
|
||||||
### 商城系统
|
### 商城系统
|
||||||
|
|
||||||
建设中...
|
建设中...
|
||||||
@ -182,6 +221,8 @@ ps:核心功能已经实现,正在对接微信小程序中...
|
|||||||
| `yudao-module-infra` | 基础设施的 Module 模块 |
|
| `yudao-module-infra` | 基础设施的 Module 模块 |
|
||||||
| `yudao-module-bpm` | 工作流程的 Module 模块 |
|
| `yudao-module-bpm` | 工作流程的 Module 模块 |
|
||||||
| `yudao-module-pay` | 支付系统的 Module 模块 |
|
| `yudao-module-pay` | 支付系统的 Module 模块 |
|
||||||
|
| `yudao-module-mall` | 商城系统的 Module 模块 |
|
||||||
|
| `yudao-module-mp` | 微信公众号的 Module 模块 |
|
||||||
| `yudao-module-visualization` | 大屏报表 Module 模块 |
|
| `yudao-module-visualization` | 大屏报表 Module 模块 |
|
||||||
|
|
||||||
### 后端
|
### 后端
|
||||||
@ -191,19 +232,19 @@ ps:核心功能已经实现,正在对接微信小程序中...
|
|||||||
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.7 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
|
| [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+ | |
|
| [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) |
|
| [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 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?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) |
|
| [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 | |
|
| [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) |
|
| [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 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) |
|
| [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) |
|
| [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/) |
|
| [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) |
|
| [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) |
|
| [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) |
|
| [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) |
|
| [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) |
|
| [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 | |
|
| [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) |
|
| [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) |
|
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.24 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
|
||||||
@ -227,7 +268,7 @@ ps:核心功能已经实现,正在对接微信小程序中...
|
|||||||
| [TypeScript](https://www.typescriptlang.org/docs/) | TypeScript | 4.9.4 |
|
| [TypeScript](https://www.typescriptlang.org/docs/) | TypeScript | 4.9.4 |
|
||||||
| [pinia](https://pinia.vuejs.org/) | vuex5 | 2.0.28 |
|
| [pinia](https://pinia.vuejs.org/) | vuex5 | 2.0.28 |
|
||||||
| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.2.2 |
|
| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.2.2 |
|
||||||
| [vxe-table](https://vxetable.cn/) | vue最强表单 | 4.3.7 |
|
| [vxe-table](https://vxetable.cn/) | vue最强表单 | 4.3.9 |
|
||||||
|
|
||||||
### [管理后台 uni-app 跨端](./yudao-ui-admin-uniapp)
|
### [管理后台 uni-app 跨端](./yudao-ui-admin-uniapp)
|
||||||
|
|
||||||
|
@ -15,6 +15,6 @@
|
|||||||
|
|
||||||
"appApi": "http://127.0.0.1:8888/app-api",
|
"appApi": "http://127.0.0.1:8888/app-api",
|
||||||
"appToken": "test1",
|
"appToken": "test1",
|
||||||
"appTenentId": "1"
|
"appTenantId": "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
pom.xml
18
pom.xml
@ -18,8 +18,9 @@
|
|||||||
<module>yudao-module-infra</module>
|
<module>yudao-module-infra</module>
|
||||||
<module>yudao-module-pay</module>
|
<module>yudao-module-pay</module>
|
||||||
<!-- <module>yudao-module-bpm</module>-->
|
<!-- <module>yudao-module-bpm</module>-->
|
||||||
<module>yudao-module-visualization</module>
|
<!-- <module>yudao-module-visualization</module>-->
|
||||||
<!-- <module>yudao-module-mall</module>-->
|
<!-- <module>yudao-module-mp</module>-->
|
||||||
|
<!-- <module>yudao-module-mall</module>-->
|
||||||
<!-- 示例项目 -->
|
<!-- 示例项目 -->
|
||||||
<module>yudao-example</module>
|
<module>yudao-example</module>
|
||||||
</modules>
|
</modules>
|
||||||
@ -29,15 +30,16 @@
|
|||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>1.6.6-snapshot</revision>
|
<revision>1.7.0-snapshot</revision>
|
||||||
<!-- Maven 相关 -->
|
<!-- Maven 相关 -->
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
|
<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 里 -->
|
<!-- 看看咋放到 bom 里 -->
|
||||||
<lombok.version>1.18.24</lombok.version>
|
<lombok.version>1.18.24</lombok.version>
|
||||||
|
<spring.boot.version>2.7.8</spring.boot.version>
|
||||||
<mapstruct.version>1.5.3.Final</mapstruct.version>
|
<mapstruct.version>1.5.3.Final</mapstruct.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
@ -64,13 +66,19 @@
|
|||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>${maven-surefire-plugin.version}</version>
|
<version>${maven-surefire-plugin.version}</version>
|
||||||
</plugin>
|
</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>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>${maven-compiler-plugin.version}</version>
|
<version>${maven-compiler-plugin.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<annotationProcessorPaths>
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</path>
|
||||||
<path>
|
<path>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
|
274
sql/mysql/optional/mp.sql
Normal file
274
sql/mysql/optional/mp.sql
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -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 = '优惠券模板';
|
|
@ -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');
|
|
||||||
|
|
@ -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 ='交易订单明细表';
|
|
@ -14,11 +14,11 @@
|
|||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>1.6.6-snapshot</revision>
|
<revision>1.7.0-snapshot</revision>
|
||||||
<!-- 统一依赖管理 -->
|
<!-- 统一依赖管理 -->
|
||||||
<spring.boot.version>2.7.7</spring.boot.version>
|
<spring.boot.version>2.7.8</spring.boot.version>
|
||||||
<!-- Web 相关 -->
|
<!-- Web 相关 -->
|
||||||
<knife4j.version>3.0.3</knife4j.version>
|
<knife4j.version>4.0.0</knife4j.version>
|
||||||
<swagger-annotations.version>1.6.8</swagger-annotations.version>
|
<swagger-annotations.version>1.6.8</swagger-annotations.version>
|
||||||
<servlet.versoin>2.5</servlet.versoin>
|
<servlet.versoin>2.5</servlet.versoin>
|
||||||
<!-- DB 相关 -->
|
<!-- DB 相关 -->
|
||||||
@ -32,7 +32,7 @@
|
|||||||
<resilience4j.version>1.7.1</resilience4j.version>
|
<resilience4j.version>1.7.1</resilience4j.version>
|
||||||
<!-- 监控相关 -->
|
<!-- 监控相关 -->
|
||||||
<skywalking.version>8.12.0</skywalking.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>
|
<opentracing.version>0.33.0</opentracing.version>
|
||||||
<!-- Test 测试相关 -->
|
<!-- Test 测试相关 -->
|
||||||
<podam.version>7.2.11.RELEASE</podam.version>
|
<podam.version>7.2.11.RELEASE</podam.version>
|
||||||
@ -41,10 +41,12 @@
|
|||||||
<!-- Bpm 工作流相关 -->
|
<!-- Bpm 工作流相关 -->
|
||||||
<flowable.version>6.8.0</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>
|
<lombok.version>1.18.24</lombok.version>
|
||||||
<mapstruct.version>1.5.3.Final</mapstruct.version>
|
<mapstruct.version>1.5.3.Final</mapstruct.version>
|
||||||
<hutool.version>5.8.11</hutool.version>
|
<hutool.version>5.8.11</hutool.version>
|
||||||
<easyexcel.verion>3.1.4</easyexcel.verion>
|
<easyexcel.verion>3.2.0</easyexcel.verion>
|
||||||
<velocity.version>2.3</velocity.version>
|
<velocity.version>2.3</velocity.version>
|
||||||
<screw.version>1.0.5</screw.version>
|
<screw.version>1.0.5</screw.version>
|
||||||
<fastjson.version>1.2.83</fastjson.version>
|
<fastjson.version>1.2.83</fastjson.version>
|
||||||
@ -54,19 +56,19 @@
|
|||||||
<commons-net.version>3.8.0</commons-net.version>
|
<commons-net.version>3.8.0</commons-net.version>
|
||||||
<jsch.version>0.1.55</jsch.version>
|
<jsch.version>0.1.55</jsch.version>
|
||||||
<tika-core.version>2.6.0</tika-core.version>
|
<tika-core.version>2.6.0</tika-core.version>
|
||||||
<aj-captcha.version>1.3.0</aj-captcha.version>
|
|
||||||
<netty-all.version>4.1.86.Final</netty-all.version>
|
<netty-all.version>4.1.86.Final</netty-all.version>
|
||||||
<ip2region.version>2.6.6</ip2region.version>
|
<ip2region.version>2.6.6</ip2region.version>
|
||||||
<!-- 三方云服务相关 -->
|
<!-- 三方云服务相关 -->
|
||||||
<okio.version>3.0.0</okio.version>
|
<okio.version>3.0.0</okio.version>
|
||||||
<okhttp3.version>4.10.0</okhttp3.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-core.version>4.6.3</aliyun-java-sdk-core.version>
|
||||||
<aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
|
<aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
|
||||||
<tencentcloud-sdk-java.version>3.1.660</tencentcloud-sdk-java.version>
|
<tencentcloud-sdk-java.version>3.1.676</tencentcloud-sdk-java.version>
|
||||||
<justauth.version>1.4.0</justauth.version>
|
<justauth.version>1.4.0</justauth.version>
|
||||||
<jimureport.version>1.5.6</jimureport.version>
|
<jimureport.version>1.5.6</jimureport.version>
|
||||||
<xercesImpl.version>2.12.2</xercesImpl.version>
|
<xercesImpl.version>2.12.2</xercesImpl.version>
|
||||||
|
<wx-java-mp.version>4.3.0</wx-java-mp.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -141,6 +143,11 @@
|
|||||||
<artifactId>yudao-spring-boot-starter-captcha</artifactId>
|
<artifactId>yudao-spring-boot-starter-captcha</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-desensitize</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Spring 核心 -->
|
<!-- Spring 核心 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -165,7 +172,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.xiaoymin</groupId>
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
|
||||||
<version>${knife4j.version}</version>
|
<version>${knife4j.version}</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
@ -446,12 +453,6 @@
|
|||||||
<version>${tika-core.version}</version>
|
<version>${tika-core.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.anji-plus</groupId>
|
|
||||||
<artifactId>spring-boot-starter-captcha</artifactId>
|
|
||||||
<version>${aj-captcha.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.velocity</groupId>
|
<groupId>org.apache.velocity</groupId>
|
||||||
<artifactId>velocity-engine-core</artifactId>
|
<artifactId>velocity-engine-core</artifactId>
|
||||||
@ -516,12 +517,24 @@
|
|||||||
<version>${netty-all.version}</version>
|
<version>${netty-all.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xingyuv</groupId>
|
||||||
|
<artifactId>spring-boot-starter-captcha-plus</artifactId>
|
||||||
|
<version>${captcha-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.lionsoul</groupId>
|
<groupId>org.lionsoul</groupId>
|
||||||
<artifactId>ip2region</artifactId>
|
<artifactId>ip2region</artifactId>
|
||||||
<version>${ip2region.version}</version>
|
<version>${ip2region.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>${jsoup.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 三方云服务相关 -->
|
<!-- 三方云服务相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.squareup.okio</groupId>
|
<groupId>com.squareup.okio</groupId>
|
||||||
@ -578,6 +591,12 @@
|
|||||||
<version>${justauth.version}</version>
|
<version>${justauth.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.binarywang</groupId>
|
||||||
|
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
|
||||||
|
<version>${wx-java-mp.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 积木报表-->
|
<!-- 积木报表-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jeecgframework.jimureport</groupId>
|
<groupId>org.jeecgframework.jimureport</groupId>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<maven.compiler.target>8</maven.compiler.target>
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<!-- 统一依赖管理 -->
|
<!-- 统一依赖管理 -->
|
||||||
<spring.boot.version>2.7.7</spring.boot.version>
|
<spring.boot.version>2.7.8</spring.boot.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<maven.compiler.target>8</maven.compiler.target>
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<!-- 统一依赖管理 -->
|
<!-- 统一依赖管理 -->
|
||||||
<spring.boot.version>2.7.7</spring.boot.version>
|
<spring.boot.version>2.7.8</spring.boot.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
<module>yudao-spring-boot-starter-flowable</module>
|
<module>yudao-spring-boot-starter-flowable</module>
|
||||||
<module>yudao-spring-boot-starter-captcha</module>
|
<module>yudao-spring-boot-starter-captcha</module>
|
||||||
<module>yudao-spring-boot-starter-websocket</module>
|
<module>yudao-spring-boot-starter-websocket</module>
|
||||||
|
<module>yudao-spring-boot-starter-desensitize</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<artifactId>yudao-framework</artifactId>
|
<artifactId>yudao-framework</artifactId>
|
||||||
|
@ -41,6 +41,11 @@ public class LocalDateTimeUtils {
|
|||||||
return LocalDateTime.of(year, mouth, day, 0, 0, 0);
|
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)};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断当前时间是否在该时间范围内
|
* 判断当前时间是否在该时间范围内
|
||||||
*
|
*
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package cn.iocoder.yudao.framework.common.util.validation;
|
package cn.iocoder.yudao.framework.common.util.validation;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import javax.validation.ConstraintViolation;
|
import javax.validation.ConstraintViolation;
|
||||||
|
@ -40,6 +40,10 @@ public class BannerApplicationRunner implements ApplicationRunner {
|
|||||||
if (isNotPresent("cn.iocoder.yudao.framework.flowable.config.YudaoFlowableConfiguration")) {
|
if (isNotPresent("cn.iocoder.yudao.framework.flowable.config.YudaoFlowableConfiguration")) {
|
||||||
System.out.println("[工作流模块 yudao-module-bpm - 已禁用][参考 https://doc.iocoder.cn/bpm/ 开启]");
|
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/ 开启]");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alipay.sdk</groupId>
|
<groupId>com.alipay.sdk</groupId>
|
||||||
<artifactId>alipay-sdk-java</artifactId>
|
<artifactId>alipay-sdk-java</artifactId>
|
||||||
<version>4.35.9.ALL</version>
|
<version>4.35.32.ALL</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.tenant.core.util;
|
|||||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 忽略租户,执行对应的逻辑
|
* 忽略租户,执行对应的逻辑
|
||||||
*
|
*
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
<!-- 三方云服务相关 -->
|
<!-- 三方云服务相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.binarywang</groupId>
|
<groupId>com.github.binarywang</groupId>
|
||||||
|
<!-- <artifactId>weixin-java-mp</artifactId>-->
|
||||||
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
|
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
|
||||||
<version>4.4.0</version>
|
<version>4.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
</description>
|
</description>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xingyuv</groupId>
|
||||||
|
<artifactId>spring-boot-starter-captcha-plus</artifactId>
|
||||||
|
</dependency>
|
||||||
<!-- Spring 核心 -->
|
<!-- Spring 核心 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@ -29,11 +33,6 @@
|
|||||||
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 验证码相关 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.anji-plus</groupId>
|
|
||||||
<artifactId>spring-boot-starter-captcha</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.captcha.config;
|
|||||||
import cn.hutool.core.util.ClassUtil;
|
import cn.hutool.core.util.ClassUtil;
|
||||||
import cn.iocoder.yudao.framework.captcha.core.enums.CaptchaRedisKeyConstants;
|
import cn.iocoder.yudao.framework.captcha.core.enums.CaptchaRedisKeyConstants;
|
||||||
import cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl;
|
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.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
package cn.iocoder.yudao.framework.captcha.core.enums;
|
package cn.iocoder.yudao.framework.captcha.core.enums;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||||
import com.anji.captcha.model.vo.PointVO;
|
import com.xingyuv.captcha.model.vo.PointVO;
|
||||||
import org.redisson.api.RLock;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
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;
|
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package cn.iocoder.yudao.framework.captcha.core.service;
|
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.AllArgsConstructor;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
@ -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>
|
@ -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();
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* 脱敏组件:支持 JSON 返回数据时,将邮箱、手机等字段进行脱敏
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.framework.desensitize.core;
|
@ -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";
|
||||||
|
}
|
@ -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 "******";
|
||||||
|
}
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 "*";
|
||||||
|
|
||||||
|
}
|
@ -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 "*";
|
||||||
|
|
||||||
|
}
|
@ -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 "*";
|
||||||
|
|
||||||
|
}
|
@ -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 "*";
|
||||||
|
|
||||||
|
}
|
@ -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 "*";
|
||||||
|
|
||||||
|
}
|
@ -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 "*";
|
||||||
|
|
||||||
|
}
|
@ -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 "*";
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 "*";
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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.PageParam;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
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 cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
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.mapper.BaseMapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||||
|
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.Collection;
|
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 实体们
|
* @param entities 实体们
|
||||||
*/
|
*/
|
||||||
default void insertBatch(Collection<T> 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) {
|
default void updateBatch(T update) {
|
||||||
update(update, new QueryWrapper<>());
|
update(update, new QueryWrapper<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void updateBatch(Collection<T> entities, int size) {
|
||||||
|
Db.updateBatchById(entities, size);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package cn.iocoder.yudao.framework.mybatis.core.query;
|
package cn.iocoder.yudao.framework.mybatis.core.query;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
|
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.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||||
|
@ -146,19 +146,19 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
|
|||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public QueryWrapperX<T> limit1() {
|
public QueryWrapperX<T> limitN(int n) {
|
||||||
Assert.notNull(SqlConstants.DB_TYPE, "获取不到数据库的类型");
|
Assert.notNull(SqlConstants.DB_TYPE, "获取不到数据库的类型");
|
||||||
switch (SqlConstants.DB_TYPE) {
|
switch (SqlConstants.DB_TYPE) {
|
||||||
case ORACLE:
|
case ORACLE:
|
||||||
case ORACLE_12C:
|
case ORACLE_12C:
|
||||||
super.eq("ROWNUM", 1);
|
super.eq("ROWNUM", n);
|
||||||
break;
|
break;
|
||||||
case SQL_SERVER:
|
case SQL_SERVER:
|
||||||
case SQL_SERVER2005:
|
case SQL_SERVER2005:
|
||||||
super.select("TOP 1 *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条,所以只好使用 * 查询剩余字段
|
super.select("TOP " + n + " *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条,所以只好使用 * 查询剩余字段
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
super.last("LIMIT 1");
|
super.last("LIMIT " + n);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,10 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import uk.co.jemos.podam.api.PodamFactory;
|
import uk.co.jemos.podam.api.PodamFactory;
|
||||||
import uk.co.jemos.podam.api.PodamFactoryImpl;
|
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.lang.reflect.Type;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -95,6 +98,10 @@ public class RandomUtils {
|
|||||||
return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus();
|
return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String randomEmail() {
|
||||||
|
return randomString() + "@qq.com";
|
||||||
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) {
|
public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) {
|
||||||
T pojo = PODAM_FACTORY.manufacturePojo(clazz);
|
T pojo = PODAM_FACTORY.manufacturePojo(clazz);
|
||||||
|
@ -21,6 +21,12 @@
|
|||||||
<artifactId>yudao-common</artifactId>
|
<artifactId>yudao-common</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Web 相关 -->
|
<!-- Web 相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@ -35,7 +41,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.xiaoymin</groupId>
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.swagger</groupId>
|
<groupId>io.swagger</groupId>
|
||||||
@ -67,6 +73,12 @@
|
|||||||
<scope>provided</scope> <!-- 设置为 provided,主要是 GlobalExceptionHandler 使用 -->
|
<scope>provided</scope> <!-- 设置为 provided,主要是 GlobalExceptionHandler 使用 -->
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- xss -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -1 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Web 框架,全局异常、API 日志等
|
||||||
|
*/
|
||||||
package cn.iocoder.yudao.framework;
|
package cn.iocoder.yudao.framework;
|
||||||
|
@ -9,15 +9,16 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import springfox.documentation.builders.ApiInfoBuilder;
|
import springfox.documentation.builders.ApiInfoBuilder;
|
||||||
import springfox.documentation.builders.ExampleBuilder;
|
import springfox.documentation.builders.ParameterBuilder;
|
||||||
import springfox.documentation.builders.PathSelectors;
|
import springfox.documentation.builders.PathSelectors;
|
||||||
import springfox.documentation.builders.RequestParameterBuilder;
|
import springfox.documentation.schema.ModelRef;
|
||||||
import springfox.documentation.service.*;
|
import springfox.documentation.service.*;
|
||||||
import springfox.documentation.spi.DocumentationType;
|
import springfox.documentation.spi.DocumentationType;
|
||||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||||
import springfox.documentation.spring.web.plugins.Docket;
|
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.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ import static springfox.documentation.builders.RequestHandlerSelectors.basePacka
|
|||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@EnableSwagger2
|
@EnableSwagger2WebMvc
|
||||||
@EnableKnife4j
|
@EnableKnife4j
|
||||||
@ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
|
@ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
|
||||||
// 允许使用 swagger.enable=false 禁用 Swagger
|
// 允许使用 swagger.enable=false 禁用 Swagger
|
||||||
@ -59,7 +60,7 @@ public class YudaoSwaggerAutoConfiguration {
|
|||||||
.securitySchemes(securitySchemes())
|
.securitySchemes(securitySchemes())
|
||||||
.securityContexts(securityContexts())
|
.securityContexts(securityContexts())
|
||||||
// ④ 全局参数(多租户 header)
|
// ④ 全局参数(多租户 header)
|
||||||
.globalRequestParameters(globalRequestParameters());
|
.globalOperationParameters(globalRequestParameters());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== apiInfo ==========
|
// ========== apiInfo ==========
|
||||||
@ -95,7 +96,7 @@ public class YudaoSwaggerAutoConfiguration {
|
|||||||
return Collections.singletonList(SecurityContext.builder()
|
return Collections.singletonList(SecurityContext.builder()
|
||||||
.securityReferences(securityReferences())
|
.securityReferences(securityReferences())
|
||||||
// 通过 PathSelectors.regex("^(?!auth).*$"),排除包含 "auth" 的接口不需要使用securitySchemes
|
// 通过 PathSelectors.regex("^(?!auth).*$"),排除包含 "auth" 的接口不需要使用securitySchemes
|
||||||
.operationSelector(o -> o.requestMappingPattern().matches("^(?!auth).*$"))
|
.forPaths(PathSelectors.regex("^(?!auth).*$"))
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,11 +110,17 @@ public class YudaoSwaggerAutoConfiguration {
|
|||||||
|
|
||||||
// ========== globalRequestParameters ==========
|
// ========== globalRequestParameters ==========
|
||||||
|
|
||||||
private static List<RequestParameter> globalRequestParameters() {
|
private static List<Parameter> globalRequestParameters() {
|
||||||
RequestParameterBuilder tenantParameter = new RequestParameterBuilder()
|
List<Parameter> tenantParameter = new ArrayList<>();
|
||||||
.name(HEADER_TENANT_ID).description("租户编号")
|
tenantParameter.add(new ParameterBuilder()
|
||||||
.in(ParameterType.HEADER).example(new ExampleBuilder().value(1L).build());
|
.name(HEADER_TENANT_ID)
|
||||||
return Collections.singletonList(tenantParameter.build());
|
.description("租户编号")
|
||||||
|
.modelRef(new ModelRef("long"))
|
||||||
|
.defaultValue("1")
|
||||||
|
.parameterType("header")
|
||||||
|
.required(true)
|
||||||
|
.build());
|
||||||
|
return tenantParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
|
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
|
||||||
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
|
|
||||||
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
|
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -20,7 +19,7 @@ public class SpringFoxHandlerProviderBeanPostProcessor implements BeanPostProces
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||||
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
|
if (bean instanceof WebMvcRequestHandlerProvider) {
|
||||||
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
|
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
|
||||||
}
|
}
|
||||||
return bean;
|
return bean;
|
||||||
|
@ -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.common.enums.WebFilterOrderEnum;
|
||||||
import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter;
|
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.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.GlobalExceptionHandler;
|
||||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
|
import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
|
||||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
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.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.util.AntPathMatcher;
|
import org.springframework.util.AntPathMatcher;
|
||||||
import org.springframework.util.PathMatcher;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
@ -27,7 +25,7 @@ import javax.annotation.Resource;
|
|||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
|
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@EnableConfigurationProperties({WebProperties.class, XssProperties.class})
|
@EnableConfigurationProperties(WebProperties.class)
|
||||||
public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
|
public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@ -48,7 +46,7 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
|
|||||||
* 设置 API 前缀,仅仅匹配 controller 包下的
|
* 设置 API 前缀,仅仅匹配 controller 包下的
|
||||||
*
|
*
|
||||||
* @param configurer 配置
|
* @param configurer 配置
|
||||||
* @param api API 配置
|
* @param api API 配置
|
||||||
*/
|
*/
|
||||||
private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) {
|
private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) {
|
||||||
AntPathMatcher antPathMatcher = new AntPathMatcher(".");
|
AntPathMatcher antPathMatcher = new AntPathMatcher(".");
|
||||||
@ -100,14 +98,6 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
|
|||||||
return createFilterBean(new CacheRequestBodyFilter(), WebFilterOrderEnum.REQUEST_BODY_CACHE_FILTER);
|
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,演示模式
|
* 创建 DemoFilter Bean,演示模式
|
||||||
*/
|
*/
|
||||||
@ -117,7 +107,7 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
|
|||||||
return createFilterBean(new DemoFilter(), WebFilterOrderEnum.DEMO_FILTER);
|
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);
|
FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
|
||||||
bean.setOrder(order);
|
bean.setOrder(order);
|
||||||
return bean;
|
return bean;
|
||||||
|
@ -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,避免 " 被转移成 " 字符
|
|
||||||
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,4 +1,4 @@
|
|||||||
package cn.iocoder.yudao.framework.web.config;
|
package cn.iocoder.yudao.framework.xss.config;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -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 lombok.AllArgsConstructor;
|
||||||
import org.springframework.util.PathMatcher;
|
import org.springframework.util.PathMatcher;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
@ -14,8 +15,6 @@ import java.io.IOException;
|
|||||||
/**
|
/**
|
||||||
* Xss 过滤器
|
* Xss 过滤器
|
||||||
*
|
*
|
||||||
* 对 Xss 不了解的胖友,可以看看 http://www.iocoder.cn/Fight/The-new-girl-asked-me-why-AJAX-requests-are-not-secure-I-did-not-answer/
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ -30,10 +29,12 @@ public class XssFilter extends OncePerRequestFilter {
|
|||||||
*/
|
*/
|
||||||
private final PathMatcher pathMatcher;
|
private final PathMatcher pathMatcher;
|
||||||
|
|
||||||
|
private final XssCleaner xssCleaner;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
filterChain.doFilter(new XssRequestWrapper(request), response);
|
filterChain.doFilter(new XssRequestWrapper(request, xssCleaner), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* 针对 XSS 的基础封装
|
||||||
|
*
|
||||||
|
* XSS 说明:https://tech.meituan.com/2018/09/27/fe-security.html
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.framework.xss;
|
@ -1,4 +1,5 @@
|
|||||||
cn.iocoder.yudao.framework.apilog.config.YudaoApiLogAutoConfiguration
|
cn.iocoder.yudao.framework.apilog.config.YudaoApiLogAutoConfiguration
|
||||||
cn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration
|
cn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration
|
||||||
cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration
|
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
|
||||||
|
@ -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 cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||||
import com.baomidou.mybatisplus.generator.config.po.TableField;
|
import com.baomidou.mybatisplus.generator.config.po.TableField;
|
||||||
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.Mappings;
|
import org.mapstruct.Mappings;
|
||||||
|
import org.mapstruct.Named;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -37,7 +39,7 @@ public interface CodegenConvert {
|
|||||||
|
|
||||||
@Mappings({
|
@Mappings({
|
||||||
@Mapping(source = "name", target = "columnName"),
|
@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 = "comment", target = "columnComment"),
|
||||||
@Mapping(source = "metaInfo.nullable", target = "nullable"),
|
@Mapping(source = "metaInfo.nullable", target = "nullable"),
|
||||||
@Mapping(source = "keyFlag", target = "primaryKey"),
|
@Mapping(source = "keyFlag", target = "primaryKey"),
|
||||||
@ -47,6 +49,11 @@ public interface CodegenConvert {
|
|||||||
})
|
})
|
||||||
CodegenColumnDO convert(TableField bean);
|
CodegenColumnDO convert(TableField bean);
|
||||||
|
|
||||||
|
@Named("getDataType")
|
||||||
|
default String getDataType(JdbcType jdbcType) {
|
||||||
|
return jdbcType.name();
|
||||||
|
}
|
||||||
|
|
||||||
// ========== CodegenTableDO 相关 ==========
|
// ========== CodegenTableDO 相关 ==========
|
||||||
|
|
||||||
// List<CodegenTableRespVO> convertList02(List<CodegenTableDO> list);
|
// List<CodegenTableRespVO> convertList02(List<CodegenTableDO> list);
|
||||||
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnu
|
|||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.generator.config.po.TableField;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
@ -29,7 +30,7 @@ public class CodegenColumnDO extends BaseDO {
|
|||||||
private Long id;
|
private Long id;
|
||||||
/**
|
/**
|
||||||
* 表编号
|
* 表编号
|
||||||
*
|
* <p>
|
||||||
* 关联 {@link CodegenTableDO#getId()}
|
* 关联 {@link CodegenTableDO#getId()}
|
||||||
*/
|
*/
|
||||||
private Long tableId;
|
private Long tableId;
|
||||||
@ -41,7 +42,8 @@ public class CodegenColumnDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private String columnName;
|
private String columnName;
|
||||||
/**
|
/**
|
||||||
* 字段类型
|
* 数据库字段类型
|
||||||
|
* 关联 {@link TableField.MetaInfo#getJdbcType()}
|
||||||
*/
|
*/
|
||||||
private String dataType;
|
private String dataType;
|
||||||
/**
|
/**
|
||||||
@ -69,7 +71,7 @@ public class CodegenColumnDO extends BaseDO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Java 属性类型
|
* Java 属性类型
|
||||||
*
|
* <p>
|
||||||
* 例如说 String、Boolean 等等
|
* 例如说 String、Boolean 等等
|
||||||
*/
|
*/
|
||||||
private String javaType;
|
private String javaType;
|
||||||
@ -79,7 +81,7 @@ public class CodegenColumnDO extends BaseDO {
|
|||||||
private String javaField;
|
private String javaField;
|
||||||
/**
|
/**
|
||||||
* 字典类型
|
* 字典类型
|
||||||
*
|
* <p>
|
||||||
* 关联 DictTypeDO 的 type 属性
|
* 关联 DictTypeDO 的 type 属性
|
||||||
*/
|
*/
|
||||||
private String dictType;
|
private String dictType;
|
||||||
@ -104,7 +106,7 @@ public class CodegenColumnDO extends BaseDO {
|
|||||||
private Boolean listOperation;
|
private Boolean listOperation;
|
||||||
/**
|
/**
|
||||||
* List 查询操作的条件类型
|
* List 查询操作的条件类型
|
||||||
*
|
* <p>
|
||||||
* 枚举 {@link CodegenColumnListConditionEnum}
|
* 枚举 {@link CodegenColumnListConditionEnum}
|
||||||
*/
|
*/
|
||||||
private String listOperationCondition;
|
private String listOperationCondition;
|
||||||
@ -117,7 +119,7 @@ public class CodegenColumnDO extends BaseDO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 显示类型
|
* 显示类型
|
||||||
*
|
* <p>
|
||||||
* 枚举 {@link CodegenColumnHtmlTypeEnum}
|
* 枚举 {@link CodegenColumnHtmlTypeEnum}
|
||||||
*/
|
*/
|
||||||
private String htmlType;
|
private String htmlType;
|
||||||
|
@ -6,15 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||||||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
|
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
|
||||||
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
|
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件配置 Mapper
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
|
public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
|
||||||
|
|
||||||
@ -26,7 +18,4 @@ public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
|
|||||||
.orderByDesc(FileConfigDO::getId));
|
.orderByDesc(FileConfigDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Select("SELECT COUNT(*) FROM infra_file_config WHERE update_time > #{maxUpdateTime}")
|
|
||||||
Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ public class FileConfigRefreshConsumer extends AbstractChannelMessageListener<Fi
|
|||||||
@Override
|
@Override
|
||||||
public void onMessage(FileConfigRefreshMessage message) {
|
public void onMessage(FileConfigRefreshMessage message) {
|
||||||
log.info("[onMessage][收到 FileConfig 刷新消息]");
|
log.info("[onMessage][收到 FileConfig 刷新消息]");
|
||||||
fileConfigService.initFileClients();
|
fileConfigService.initLocalCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -237,10 +237,6 @@ public class CodegenServiceImpl implements CodegenService {
|
|||||||
@Override
|
@Override
|
||||||
public List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {
|
public List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {
|
||||||
List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId, name, comment);
|
List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId, name, comment);
|
||||||
// 移除置顶前缀的表名 // TODO 未来做成可配置
|
|
||||||
tables.removeIf(table -> table.getName().toUpperCase().startsWith("QRTZ_"));
|
|
||||||
tables.removeIf(table -> table.getName().toUpperCase().startsWith("ACT_"));
|
|
||||||
tables.removeIf(table -> table.getName().toUpperCase().startsWith("FLW_"));
|
|
||||||
// 移除已经生成的表
|
// 移除已经生成的表
|
||||||
// 移除在 Codegen 中,已经存在的
|
// 移除在 Codegen 中,已经存在的
|
||||||
Set<String> existsTables = CollectionUtils.convertSet(
|
Set<String> existsTables = CollectionUtils.convertSet(
|
||||||
|
@ -52,7 +52,11 @@ public class DatabaseTableServiceImpl implements DatabaseTableService {
|
|||||||
StrategyConfig.Builder strategyConfig = new StrategyConfig.Builder();
|
StrategyConfig.Builder strategyConfig = new StrategyConfig.Builder();
|
||||||
if (StrUtil.isNotEmpty(name)) {
|
if (StrUtil.isNotEmpty(name)) {
|
||||||
strategyConfig.addInclude(name);
|
strategyConfig.addInclude(name);
|
||||||
|
} else {
|
||||||
|
// 移除工作流和定时任务前缀的表名 // TODO 未来做成可配置
|
||||||
|
strategyConfig.addExclude("ACT_[\\S\\s]+|QRTZ_[\\S\\s]+|FLW_[\\S\\s]+");
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.TIME_PACK).build(); // 只使用 Date 类型,不使用 LocalDate
|
GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.TIME_PACK).build(); // 只使用 Date 类型,不使用 LocalDate
|
||||||
ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, strategyConfig.build(),
|
ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, strategyConfig.build(),
|
||||||
null, globalConfig, null);
|
null, globalConfig, null);
|
||||||
|
@ -21,7 +21,7 @@ public interface FileConfigService {
|
|||||||
/**
|
/**
|
||||||
* 初始化文件客户端
|
* 初始化文件客户端
|
||||||
*/
|
*/
|
||||||
void initFileClients();
|
void initLocalCache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建文件配置
|
* 创建文件配置
|
||||||
|
@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.infra.service.file;
|
|||||||
import cn.hutool.core.io.resource.ResourceUtil;
|
import cn.hutool.core.io.resource.ResourceUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||||
import cn.iocoder.yudao.framework.file.core.client.FileClient;
|
import cn.iocoder.yudao.framework.file.core.client.FileClient;
|
||||||
@ -19,7 +18,6 @@ import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;
|
|||||||
import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer;
|
import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.support.TransactionSynchronization;
|
import org.springframework.transaction.support.TransactionSynchronization;
|
||||||
@ -29,7 +27,6 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.validation.Validator;
|
import javax.validation.Validator;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -48,18 +45,6 @@ import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class FileConfigServiceImpl implements FileConfigService {
|
public class FileConfigServiceImpl implements FileConfigService {
|
||||||
|
|
||||||
/**
|
|
||||||
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期
|
|
||||||
* 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
|
|
||||||
*/
|
|
||||||
private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
private volatile LocalDateTime maxUpdateTime;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private FileClientFactory fileClientFactory;
|
private FileClientFactory fileClientFactory;
|
||||||
/**
|
/**
|
||||||
@ -79,34 +64,12 @@ public class FileConfigServiceImpl implements FileConfigService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void initFileClients() {
|
public void initLocalCache() {
|
||||||
initLocalCacheIfUpdate(null);
|
// 第一步:查询数据
|
||||||
}
|
|
||||||
|
|
||||||
@Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
|
|
||||||
public void schedulePeriodicRefresh() {
|
|
||||||
initLocalCacheIfUpdate(this.maxUpdateTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新本地缓存
|
|
||||||
*
|
|
||||||
* @param maxUpdateTime 最大更新时间
|
|
||||||
* 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
|
|
||||||
* 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
|
|
||||||
*/
|
|
||||||
private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
|
|
||||||
// 第一步:基于 maxUpdateTime 判断缓存是否刷新。
|
|
||||||
// 如果没有增量的数据变化,则不进行本地缓存的刷新
|
|
||||||
if (maxUpdateTime != null
|
|
||||||
&& fileConfigMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
|
|
||||||
log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<FileConfigDO> configs = fileConfigMapper.selectList();
|
List<FileConfigDO> configs = fileConfigMapper.selectList();
|
||||||
log.info("[initLocalCacheIfUpdate][缓存文件配置,数量为:{}]", configs.size());
|
log.info("[initLocalCache][缓存文件配置,数量为:{}]", configs.size());
|
||||||
|
|
||||||
// 第二步:构建缓存。创建或更新文件 Client
|
// 第二步:构建缓存:创建或更新文件 Client
|
||||||
configs.forEach(config -> {
|
configs.forEach(config -> {
|
||||||
fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig());
|
fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig());
|
||||||
// 如果是 master,进行设置
|
// 如果是 master,进行设置
|
||||||
@ -114,9 +77,6 @@ public class FileConfigServiceImpl implements FileConfigService {
|
|||||||
masterFileClient = fileClientFactory.getFileClient(config.getId());
|
masterFileClient = fileClientFactory.getFileClient(config.getId());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
|
|
||||||
this.maxUpdateTime = CollectionUtils.getMaxValue(configs, FileConfigDO::getUpdateTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -79,7 +79,7 @@ public class TestDemoServiceImpl implements TestDemoService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<TestDemoDO> getTestDemoPage(TestDemoPageReqVO pageReqVO) {
|
public PageResult<TestDemoDO> getTestDemoPage(TestDemoPageReqVO pageReqVO) {
|
||||||
// testDemoMapper.selectList2();
|
testDemoMapper.selectList2();
|
||||||
return testDemoMapper.selectPage(pageReqVO);
|
return testDemoMapper.selectPage(pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
import { reactive } from 'vue'
|
import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { required } from '@/utils/formRules'
|
|
||||||
import { VxeCrudSchema, useVxeCrudSchemas } from '@/hooks/web/useVxeCrudSchemas'
|
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
// 表单校验
|
// 表单校验
|
||||||
export const rules = reactive({
|
export const rules = reactive({
|
||||||
|
@ -75,11 +75,6 @@
|
|||||||
</XModal>
|
</XModal>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts" name="${simpleClassName}">
|
<script setup lang="ts" name="${simpleClassName}">
|
||||||
// 全局相关的 import
|
|
||||||
import { ref, unref } from 'vue'
|
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
|
||||||
import { useMessage } from '@/hooks/web/useMessage'
|
|
||||||
import { useXTable } from '@/hooks/web/useXTable'
|
|
||||||
import { FormExpose } from '@/components/Form'
|
import { FormExpose } from '@/components/Form'
|
||||||
// 业务相关的 import
|
// 业务相关的 import
|
||||||
import { rules, allSchemas } from './${classNameVar}.data'
|
import { rules, allSchemas } from './${classNameVar}.data'
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package cn.iocoder.yudao.module.infra.service;
|
package cn.iocoder.yudao.module.infra.service;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.generator.IDatabaseQuery.DefaultDatabaseQuery;
|
import com.baomidou.mybatisplus.generator.query.DefaultQuery;
|
||||||
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
|
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
|
||||||
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
|
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
|
||||||
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
||||||
@ -19,7 +19,7 @@ public class DefaultDatabaseQueryTest {
|
|||||||
|
|
||||||
ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null);
|
ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null);
|
||||||
|
|
||||||
DefaultDatabaseQuery query = new DefaultDatabaseQuery(builder);
|
DefaultQuery query = new DefaultQuery(builder);
|
||||||
|
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
List<TableInfo> tableInfos = query.queryTables();
|
List<TableInfo> tableInfos = query.queryTables();
|
||||||
|
@ -30,7 +30,6 @@ import java.util.Map;
|
|||||||
import static cn.hutool.core.util.RandomUtil.randomEle;
|
import static cn.hutool.core.util.RandomUtil.randomEle;
|
||||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
|
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||||
@ -74,16 +73,13 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
|
|||||||
when(fileClientFactory.getFileClient(eq(1L))).thenReturn(masterFileClient);
|
when(fileClientFactory.getFileClient(eq(1L))).thenReturn(masterFileClient);
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
fileConfigService.initFileClients();
|
fileConfigService.initLocalCache();
|
||||||
// 断言 fileClientFactory 调用
|
// 断言 fileClientFactory 调用
|
||||||
verify(fileClientFactory).createOrUpdateFileClient(eq(1L),
|
verify(fileClientFactory).createOrUpdateFileClient(eq(1L),
|
||||||
eq(configDO1.getStorage()), eq(configDO1.getConfig()));
|
eq(configDO1.getStorage()), eq(configDO1.getConfig()));
|
||||||
verify(fileClientFactory).createOrUpdateFileClient(eq(2L),
|
verify(fileClientFactory).createOrUpdateFileClient(eq(2L),
|
||||||
eq(configDO2.getStorage()), eq(configDO2.getConfig()));
|
eq(configDO2.getStorage()), eq(configDO2.getConfig()));
|
||||||
assertSame(masterFileClient, fileConfigService.getMasterFileClient());
|
assertSame(masterFileClient, fileConfigService.getMasterFileClient());
|
||||||
// 断言 maxUpdateTime 缓存
|
|
||||||
assertEquals(max(configDO1.getUpdateTime(), configDO2.getUpdateTime()),
|
|
||||||
fileConfigService.getMaxUpdateTime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
24
yudao-module-mp/pom.xml
Normal file
24
yudao-module-mp/pom.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>yudao</artifactId>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>yudao-module-mp</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
wechat 模块,主要实现微信平台的相关业务。
|
||||||
|
例如:微信公众号、企业微信 SCRM 等
|
||||||
|
</description>
|
||||||
|
<modules>
|
||||||
|
<module>yudao-module-mp-api</module>
|
||||||
|
<module>yudao-module-mp-biz</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
</project>
|
26
yudao-module-mp/yudao-module-mp-api/pom.xml
Normal file
26
yudao-module-mp/yudao-module-mp-api/pom.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>yudao-module-mp</artifactId>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>yudao-module-mp-api</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>
|
||||||
|
mp 模块 API,暴露给其它模块调用
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,64 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.enums;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mp 错误码枚举类
|
||||||
|
*
|
||||||
|
* mp 系统,使用 1-006-000-000 段
|
||||||
|
*/
|
||||||
|
public interface ErrorCodeConstants {
|
||||||
|
|
||||||
|
// ========== 公众号账号 1006000000============
|
||||||
|
ErrorCode ACCOUNT_NOT_EXISTS = new ErrorCode(1006000000, "公众号账号不存在");
|
||||||
|
ErrorCode ACCOUNT_GENERATE_QR_CODE_FAIL = new ErrorCode(1006000001, "生成公众号二维码失败,原因:{}");
|
||||||
|
ErrorCode ACCOUNT_CLEAR_QUOTA_FAIL = new ErrorCode(1006000001, "清空公众号的 API 配额失败,原因:{}");
|
||||||
|
|
||||||
|
// ========== 公众号统计 1006001000============
|
||||||
|
ErrorCode STATISTICS_GET_USER_SUMMARY_FAIL = new ErrorCode(1006001000, "获取粉丝增减数据失败,原因:{}");
|
||||||
|
ErrorCode STATISTICS_GET_USER_CUMULATE_FAIL = new ErrorCode(1006001001, "获得粉丝累计数据失败,原因:{}");
|
||||||
|
ErrorCode STATISTICS_GET_UPSTREAM_MESSAGE_FAIL = new ErrorCode(1006001002, "获得消息发送概况数据失败,原因:{}");
|
||||||
|
ErrorCode STATISTICS_GET_INTERFACE_SUMMARY_FAIL = new ErrorCode(1006001003, "获得接口分析数据失败,原因:{}");
|
||||||
|
|
||||||
|
// ========== 公众号标签 1006002000============
|
||||||
|
ErrorCode TAG_NOT_EXISTS = new ErrorCode(1006002000, "标签不存在");
|
||||||
|
ErrorCode TAG_CREATE_FAIL = new ErrorCode(1006002001, "创建标签失败,原因:{}");
|
||||||
|
ErrorCode TAG_UPDATE_FAIL = new ErrorCode(1006002001, "更新标签失败,原因:{}");
|
||||||
|
ErrorCode TAG_DELETE_FAIL = new ErrorCode(1006002001, "删除标签失败,原因:{}");
|
||||||
|
ErrorCode TAG_GET_FAIL = new ErrorCode(1006002001, "获得标签失败,原因:{}");
|
||||||
|
|
||||||
|
// ========== 公众号粉丝 1006003000============
|
||||||
|
ErrorCode USER_NOT_EXISTS = new ErrorCode(1006003000, "粉丝不存在");
|
||||||
|
ErrorCode USER_UPDATE_TAG_FAIL = new ErrorCode(1006003001, "更新粉丝标签失败,原因:{}");
|
||||||
|
|
||||||
|
// ========== 公众号素材 1006004000============
|
||||||
|
ErrorCode MATERIAL_NOT_EXISTS = new ErrorCode(1006004000, "素材不存在");
|
||||||
|
ErrorCode MATERIAL_UPLOAD_FAIL = new ErrorCode(1006004001, "上传素材失败,原因:{}");
|
||||||
|
ErrorCode MATERIAL_IMAGE_UPLOAD_FAIL = new ErrorCode(1006004002, "上传图片失败,原因:{}");
|
||||||
|
ErrorCode MATERIAL_DELETE_FAIL = new ErrorCode(1006004003, "删除素材失败,原因:{}");
|
||||||
|
|
||||||
|
// ========== 公众号消息 1006005000============
|
||||||
|
ErrorCode MESSAGE_SEND_FAIL = new ErrorCode(1006005000, "发送消息失败,原因:{}");
|
||||||
|
|
||||||
|
// ========== 公众号发布能力 1006006000============
|
||||||
|
ErrorCode FREE_PUBLISH_LIST_FAIL = new ErrorCode(1006006000, "获得已成功发布列表失败,原因:{}");
|
||||||
|
ErrorCode FREE_PUBLISH_SUBMIT_FAIL = new ErrorCode(1006006001, "提交发布失败,原因:{}");
|
||||||
|
ErrorCode FREE_PUBLISH_DELETE_FAIL = new ErrorCode(1006006001, "删除发布失败,原因:{}");
|
||||||
|
|
||||||
|
// ========== 公众号草稿 1006007000============
|
||||||
|
ErrorCode DRAFT_LIST_FAIL = new ErrorCode(1006007000, "获得草稿列表失败,原因:{}");
|
||||||
|
ErrorCode DRAFT_CREATE_FAIL = new ErrorCode(1006007001, "创建草稿失败,原因:{}");
|
||||||
|
ErrorCode DRAFT_UPDATE_FAIL = new ErrorCode(1006007002, "更新草稿失败,原因:{}");
|
||||||
|
ErrorCode DRAFT_DELETE_FAIL = new ErrorCode(1006007002, "删除草稿失败,原因:{}");
|
||||||
|
|
||||||
|
// ========== 公众号菜单 1006008000============
|
||||||
|
ErrorCode MENU_SAVE_FAIL = new ErrorCode(1006008000, "创建菜单失败,原因:{}");
|
||||||
|
ErrorCode MENU_DELETE_FAIL = new ErrorCode(1006008001, "删除菜单失败,原因:{}");
|
||||||
|
|
||||||
|
// ========== 公众号自动回复 1006009000============
|
||||||
|
ErrorCode AUTO_REPLY_NOT_EXISTS = new ErrorCode(1006009000, "自动回复不存在");
|
||||||
|
ErrorCode AUTO_REPLY_ADD_SUBSCRIBE_FAIL_EXISTS = new ErrorCode(1006009001, "操作失败,原因:已存在关注时的回复");
|
||||||
|
ErrorCode AUTO_REPLY_ADD_MESSAGE_FAIL_EXISTS = new ErrorCode(1006009002, "操作失败,原因:已存在该消息类型的回复");
|
||||||
|
ErrorCode AUTO_REPLY_ADD_KEYWORD_FAIL_EXISTS = new ErrorCode(1006009003, "操作失败,原因:已关在该关键字的回复");
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.enums.message;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公众号消息自动回复的匹配模式
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum MpAutoReplyMatchEnum {
|
||||||
|
|
||||||
|
ALL(1, "完全匹配"),
|
||||||
|
LIKE(2, "半匹配"),
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配
|
||||||
|
*/
|
||||||
|
private final Integer match;
|
||||||
|
/**
|
||||||
|
* 匹配的名字
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.enums.message;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公众号消息自动回复的类型
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum MpAutoReplyTypeEnum {
|
||||||
|
|
||||||
|
SUBSCRIBE(1, "关注时回复"),
|
||||||
|
MESSAGE(2, "收到消息回复"),
|
||||||
|
KEYWORD(3, "关键词回复"),
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 来源
|
||||||
|
*/
|
||||||
|
private final Integer type;
|
||||||
|
/**
|
||||||
|
* 类型的名字
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.enums.message;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信公众号消息的发送来源
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum MpMessageSendFromEnum {
|
||||||
|
|
||||||
|
USER_TO_MP(1, "粉丝发送给公众号"),
|
||||||
|
MP_TO_USER(2, "公众号发给粉丝"),
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 来源
|
||||||
|
*/
|
||||||
|
private final Integer from;
|
||||||
|
/**
|
||||||
|
* 来源的名字
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* mp 模块,我们放微信微信公众号。
|
||||||
|
* 例如说:提供微信公众号的账号、菜单、粉丝、标签、消息、自动回复、素材、模板通知、运营数据等功能
|
||||||
|
*
|
||||||
|
* 1. Controller URL:以 /mp/ 开头,避免和其它 Module 冲突
|
||||||
|
* 2. DataObject 表名:以 mp_ 开头,方便在数据库中区分
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.module.mp;
|
89
yudao-module-mp/yudao-module-mp-biz/pom.xml
Normal file
89
yudao-module-mp/yudao-module-mp-biz/pom.xml
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>yudao-module-mp</artifactId>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>yudao-module-mp-biz</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>
|
||||||
|
mp 模块,我们放微信微信公众号。
|
||||||
|
例如说:提供微信公众号的账号、菜单、粉丝、标签、消息、自动回复、素材、模板通知、运营数据等功能
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-module-mp-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-module-system-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-module-infra-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 业务组件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-weixin</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Web 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- DB 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 消息队列相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-mq</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test 测试相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 工具类相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,98 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.controller.admin.account;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.module.mp.controller.admin.account.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.mp.convert.account.MpAccountConvert;
|
||||||
|
import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
|
||||||
|
import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
|
||||||
|
@Api(tags = "管理后台 - 公众号账号")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/mp/account")
|
||||||
|
@Validated
|
||||||
|
public class MpAccountController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MpAccountService mpAccountService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@ApiOperation("创建公众号账号")
|
||||||
|
@PreAuthorize("@ss.hasPermission('mp:account:create')")
|
||||||
|
public CommonResult<Long> createAccount(@Valid @RequestBody MpAccountCreateReqVO createReqVO) {
|
||||||
|
return success(mpAccountService.createAccount(createReqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@ApiOperation("更新公众号账号")
|
||||||
|
@PreAuthorize("@ss.hasPermission('mp:account:update')")
|
||||||
|
public CommonResult<Boolean> updateAccount(@Valid @RequestBody MpAccountUpdateReqVO updateReqVO) {
|
||||||
|
mpAccountService.updateAccount(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@ApiOperation("删除公众号账号")
|
||||||
|
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
|
||||||
|
@PreAuthorize("@ss.hasPermission('mp:account:delete')")
|
||||||
|
public CommonResult<Boolean> deleteAccount(@RequestParam("id") Long id) {
|
||||||
|
mpAccountService.deleteAccount(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@ApiOperation("获得公众号账号")
|
||||||
|
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||||
|
@PreAuthorize("@ss.hasPermission('mp:account:query')")
|
||||||
|
public CommonResult<MpAccountRespVO> getAccount(@RequestParam("id") Long id) {
|
||||||
|
MpAccountDO wxAccount = mpAccountService.getAccount(id);
|
||||||
|
return success(MpAccountConvert.INSTANCE.convert(wxAccount));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@ApiOperation("获得公众号账号分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('mp:account:query')")
|
||||||
|
public CommonResult<PageResult<MpAccountRespVO>> getAccountPage(@Valid MpAccountPageReqVO pageVO) {
|
||||||
|
PageResult<MpAccountDO> pageResult = mpAccountService.getAccountPage(pageVO);
|
||||||
|
return success(MpAccountConvert.INSTANCE.convertPage(pageResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/list-all-simple")
|
||||||
|
@ApiOperation(value = "获取公众号账号精简信息列表")
|
||||||
|
@PreAuthorize("@ss.hasPermission('mp:account:query')")
|
||||||
|
public CommonResult<List<MpAccountSimpleRespVO>> getSimpleAccounts() {
|
||||||
|
List<MpAccountDO> list = mpAccountService.getAccountList();
|
||||||
|
return success(MpAccountConvert.INSTANCE.convertList02(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/generate-qr-code")
|
||||||
|
@ApiOperation("生成公众号二维码")
|
||||||
|
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
|
||||||
|
@PreAuthorize("@ss.hasPermission('mp:account:qr-code')")
|
||||||
|
public CommonResult<Boolean> generateAccountQrCode(@RequestParam("id") Long id) {
|
||||||
|
mpAccountService.generateAccountQrCode(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/clear-quota")
|
||||||
|
@ApiOperation("清空公众号 API 配额")
|
||||||
|
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
|
||||||
|
@PreAuthorize("@ss.hasPermission('mp:account:clear-quota')")
|
||||||
|
public CommonResult<Boolean> clearAccountQuota(@RequestParam("id") Long id) {
|
||||||
|
mpAccountService.clearAccountQuota(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.controller.admin.account.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公众号账号 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||||
|
*
|
||||||
|
* @author fengdan
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class MpAccountBaseVO {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "公众号名称", required = true, example = "芋道源码")
|
||||||
|
@NotEmpty(message = "公众号名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "公众号微信号", required = true, example = "yudaoyuanma")
|
||||||
|
@NotEmpty(message = "公众号微信号不能为空")
|
||||||
|
private String account;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "公众号 appId", required = true, example = "wx5b23ba7a5589ecbb")
|
||||||
|
@NotEmpty(message = "公众号 appId 不能为空")
|
||||||
|
private String appId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "公众号密钥", required = true, example = "3a7b3b20c537e52e74afd395eb85f61f")
|
||||||
|
@NotEmpty(message = "公众号密钥不能为空")
|
||||||
|
private String appSecret;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "公众号 token", required = true, example = "kangdayuzhen")
|
||||||
|
@NotEmpty(message = "公众号 token 不能为空")
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "加密密钥", example = "gjN+Ksei")
|
||||||
|
private String aesKey;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "备注", example = "请关注芋道源码,学习技术")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.controller.admin.account.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ApiModel("管理后台 - 公众号账号创建 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class MpAccountCreateReqVO extends MpAccountBaseVO {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.controller.admin.account.vo;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import io.swagger.annotations.*;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||||
|
|
||||||
|
@ApiModel("管理后台 - 公众号账号分页 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class MpAccountPageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "公众号名称", notes = "模糊匹配")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "公众号账号", notes = "模糊匹配")
|
||||||
|
private String account;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "公众号 appid", notes = "模糊匹配")
|
||||||
|
private String appId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.controller.admin.account.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@ApiModel("管理后台 - 公众号账号 Response VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class MpAccountRespVO extends MpAccountBaseVO {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "编号", required = true, example = "1024")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "二维码图片URL", example = "https://www.iocoder.cn/1024.png")
|
||||||
|
private String qrCodeUrl;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "创建时间", required = true)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package cn.iocoder.yudao.module.mp.controller.admin.account.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@ApiModel("管理后台 - 公众号账号精简信息 Response VO")
|
||||||
|
@Data
|
||||||
|
public class MpAccountSimpleRespVO {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "编号", required = true, example = "1024")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "公众号名称", required = true, example = "芋道源码")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user