zhousq_java
1 month ago
commit
3178743ba7
1101 changed files with 108500 additions and 0 deletions
@ -0,0 +1,53 @@ |
|||
###################################################################### |
|||
# Build Tools |
|||
|
|||
.gradle |
|||
/build/ |
|||
!gradle/wrapper/gradle-wrapper.jar |
|||
|
|||
target/ |
|||
!.mvn/wrapper/maven-wrapper.jar |
|||
|
|||
.flattened-pom.xml |
|||
|
|||
###################################################################### |
|||
# IDE |
|||
|
|||
### STS ### |
|||
.apt_generated |
|||
.classpath |
|||
.factorypath |
|||
.project |
|||
.settings |
|||
.springBeans |
|||
|
|||
### IntelliJ IDEA ### |
|||
.idea |
|||
*.iws |
|||
*.iml |
|||
*.ipr |
|||
|
|||
### NetBeans ### |
|||
nbproject/private/ |
|||
build/* |
|||
nbbuild/ |
|||
dist/ |
|||
nbdist/ |
|||
.nb-gradle/ |
|||
|
|||
###################################################################### |
|||
# Others |
|||
*.log |
|||
*.xml.versionsBackup |
|||
*.swp |
|||
|
|||
!*/build/*.java |
|||
!*/build/*.html |
|||
!*/build/*.xml |
|||
|
|||
### JRebel ### |
|||
rebel.xml |
|||
|
|||
application-my.yaml |
|||
|
|||
/win-ui-app/unpackage/ |
@ -0,0 +1,20 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2021 ruoyi-vue-pro |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy of |
|||
this software and associated documentation files (the "Software"), to deal in |
|||
the Software without restriction, including without limitation the rights to |
|||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
|||
the Software, and to permit persons to whom the Software is furnished to do so, |
|||
subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
|||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
|||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
|||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
|||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,364 @@ |
|||
<p align="center"> |
|||
<img src="https://img.shields.io/badge/Spring%20Boot-3.3.4-blue.svg" alt="Downloads"> |
|||
<img src="https://img.shields.io/badge/Vue-3.2-blue.svg" alt="Downloads"> |
|||
<img src="https://img.shields.io/github/license/YunaiV/ruoyi-vue-pro" alt="Downloads" /> |
|||
</p> |
|||
|
|||
**严肃声明:现在、未来都不会有商业版本,所有代码全部开源!!** |
|||
|
|||
**「我喜欢写代码,乐此不疲」** |
|||
**「我喜欢做开源,以此为乐」** |
|||
|
|||
我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。 |
|||
|
|||
如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 |
|||
|
|||
## 🐶 新手必读 |
|||
|
|||
* 演示地址【Vue3 + element-plus】:<http://dashboard-vue3.win.iocoder.cn> |
|||
* 演示地址【Vue3 + vben(ant-design-vue)】:<http://dashboard-vben.win.iocoder.cn> |
|||
* 演示地址【Vue2 + element-ui】:<http://dashboard.win.iocoder.cn> |
|||
* 启动文档:<https://doc.iocoder.cn/quick-start/> |
|||
* 视频教程:<https://doc.iocoder.cn/video/> |
|||
|
|||
## 🐰 版本说明 |
|||
|
|||
| 版本 | JDK 8 + Spring Boot 2.7 | JDK 17/21 + Spring Boot 3.2 | |
|||
|---------------------------------------------------------------------|---------------------------------------------------------------------------|---------------------------------------------------------------------------------------| |
|||
| 【完整版】[ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-jdk17/) 分支 | |
|||
| 【精简版】[win-boot-mini](https://gitee.com/wincode/win-boot-mini) | [`master`](https://gitee.com/wincode/win-boot-mini/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/wincode/win-boot-mini/tree/master-jdk17/) 分支 | |
|||
|
|||
* 【完整版】:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能 |
|||
* 【精简版】:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能 |
|||
|
|||
可参考 [《迁移文档》](https://doc.iocoder.cn/migrate-module/) ,只需要 5-10 分钟,即可将【完整版】按需迁移到【精简版】 |
|||
|
|||
## 🐯 平台简介 |
|||
|
|||
**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。 |
|||
|
|||
> 有任何问题,或者想要的功能,可以在 _Issues_ 中提给艿艿。 |
|||
> |
|||
> 😜 给项目点点 Star 吧,这对我们真的很重要! |
|||
|
|||
![架构图](/.image/common/ruoyi-vue-pro-architecture.png) |
|||
|
|||
* Java 后端:`master` 分支为 JDK 8 + Spring Boot 2.7,`master-jdk17` 分支为 JDK 17/21 + Spring Boot 3.2 |
|||
* 管理后台的电脑端:Vue3 提供 `element-plus`、`vben(ant-design-vue)` 两个版本,Vue2 提供 `element-ui` 版本 |
|||
* 管理后台的移动端:采用 `uni-app` 方案,一份代码多终端适配,同时支持 APP、小程序、H5! |
|||
* 后端采用 Spring Boot 多模块架构、MySQL + MyBatis Plus、Redis + Redisson |
|||
* 数据库可使用 MySQL、Oracle、PostgreSQL、SQL Server、MariaDB、国产达梦 DM、TiDB 等 |
|||
* 消息队列可使用 Event、Redis、RabbitMQ、Kafka、RocketMQ 等 |
|||
* 权限认证使用 Spring Security & Token & Redis,支持多终端、多种用户的认证系统,支持 SSO 单点登录 |
|||
* 支持加载动态权限菜单,按钮级别权限控制,Redis 缓存提升性能 |
|||
* 支持 SaaS 多租户,可自定义每个租户的权限,提供透明化的多租户底层封装 |
|||
* 工作流使用 Flowable,支持动态表单、在线设计流程、会签 / 或签、多种任务分配方式 |
|||
* 高效率开发,使用代码生成器可以一键生成 Java、Vue 前后端代码、SQL 脚本、接口文档,支持单表、树表、主子表 |
|||
* 实时通信,采用 Spring WebSocket 实现,内置 Token 身份校验,支持 WebSocket 集群 |
|||
* 集成微信小程序、微信公众号、企业微信、钉钉等三方登陆,集成支付宝、微信等支付与退款 |
|||
* 集成阿里云、腾讯云等短信渠道,集成 MinIO、阿里云、腾讯云、七牛云等云存储服务 |
|||
* 集成报表设计器、大屏设计器,通过拖拽即可生成酷炫的报表与大屏 |
|||
|
|||
## 🐳 项目关系 |
|||
|
|||
![架构演进](/.image/common/win-roadmap.png) |
|||
|
|||
三个项目的功能对比,可见社区共同整理的 [国产开源项目对比](https://www.yuque.com/xiatian-bsgny/lm0ec1/wqf8mn) 表格。 |
|||
|
|||
### 后端项目 |
|||
|
|||
| 项目 | Star | 简介 | |
|||
|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------| |
|||
| [ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [![Gitee star](https://gitee.com/zhijiantianya/ruoyi-vue-pro/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/ruoyi-vue-pro) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/ruoyi-vue-pro.svg?style=social&label=Stars)](https://github.com/YunaiV/ruoyi-vue-pro) | 基于 Spring Boot 多模块架构 | |
|||
| [win-cloud](https://gitee.com/zhijiantianya/win-cloud) | [![Gitee star](https://gitee.com/zhijiantianya/win-cloud/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/win-cloud) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/win-cloud.svg?style=social&label=Stars)](https://github.com/YunaiV/win-cloud) | 基于 Spring Cloud 微服务架构 | |
|||
| [Spring-Boot-Labs](https://gitee.com/wincode/SpringBoot-Labs) | [![Gitee star](https://gitee.com/wincode/SpringBoot-Labs/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/win-cloud) [![GitHub stars](https://img.shields.io/github/stars/wincode/SpringBoot-Labs.svg?style=social&label=Stars)](https://github.com/wincode/SpringBoot-Labs) | 系统学习 Spring Boot & Cloud 专栏 | |
|||
|
|||
### 前端项目 |
|||
|
|||
| 项目 | Star | 简介 | |
|||
|----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------| |
|||
| [win-ui-admin-vue3](https://gitee.com/wincode/win-ui-admin-vue3) | [![Gitee star](https://gitee.com/wincode/win-ui-admin-vue3/badge/star.svg?theme=white)](https://gitee.com/wincode/win-ui-admin-vue3) [![GitHub stars](https://img.shields.io/github/stars/wincode/win-ui-admin-vue3.svg?style=social&label=Stars)](https://github.com/wincode/win-ui-admin-vue3) | 基于 Vue3 + element-plus 实现的管理后台 | |
|||
| [win-ui-admin-vben](https://gitee.com/wincode/win-ui-admin-vben) | [![Gitee star](https://gitee.com/wincode/win-ui-admin-vben/badge/star.svg?theme=white)](https://gitee.com/wincode/win-ui-admin-vben) [![GitHub stars](https://img.shields.io/github/stars/wincode/win-ui-admin-vben.svg?style=social&label=Stars)](https://github.com/wincode/win-ui-admin-vben) | 基于 Vue3 + vben(ant-design-vue) 实现的管理后台 | |
|||
| [win-mall-uniapp](https://gitee.com/wincode/win-mall-uniapp) | [![Gitee star](https://gitee.com/wincode/win-mall-uniapp/badge/star.svg?theme=white)](https://gitee.com/wincode/win-mall-uniapp) [![GitHub stars](https://img.shields.io/github/stars/wincode/win-mall-uniapp.svg?style=social&label=Stars)](https://github.com/wincode/win-mall-uniapp) | 基于 uni-app 实现的商城小程序 | |
|||
| [win-ui-admin-vue2](https://gitee.com/wincode/win-ui-admin-vue2) | [![Gitee star](https://gitee.com/wincode/win-ui-admin-vue2/badge/star.svg?theme=white)](https://gitee.com/wincode/win-ui-admin-vue2) [![GitHub stars](https://img.shields.io/github/stars/wincode/win-ui-admin-vue2.svg?style=social&label=Stars)](https://github.com/wincode/win-ui-admin-vue2) | 基于 Vue2 + element-ui 实现的管理后台 | |
|||
| [win-ui-admin-uniapp](https://gitee.com/wincode/win-ui-admin-uniapp) | [![Gitee star](https://gitee.com/wincode/win-ui-admin-uniapp/badge/star.svg?theme=white)](https://gitee.com/wincode/win-ui-admin-uniapp) [![GitHub stars](https://img.shields.io/github/stars/wincode/win-ui-admin-uniapp.svg?style=social&label=Stars)](https://github.com/wincode/win-ui-admin-uniapp) | 基于 Vue2 + element-ui 实现的管理后台 | |
|||
| [win-ui-go-view](https://gitee.com/wincode/win-ui-go-view) | [![Gitee star](https://gitee.com/wincode/win-ui-go-view/badge/star.svg?theme=white)](https://gitee.com/wincode/win-ui-go-view) [![GitHub stars](https://img.shields.io/github/stars/wincode/win-ui-go-view.svg?style=social&label=Stars)](https://github.com/wincode/win-ui-go-view) | 基于 Vue3 + naive-ui 实现的大屏报表 | |
|||
|
|||
## 😎 开源协议 |
|||
|
|||
**为什么推荐使用本项目?** |
|||
|
|||
① 本项目采用比 Apache 2.0 更宽松的 [MIT License](https://gitee.com/zhijiantianya/ruoyi-vue-pro/blob/master/LICENSE) 开源协议,个人与企业可 100% 免费使用,不用保留类作者、Copyright 信息。 |
|||
|
|||
② 代码全部开源,不会像其他项目一样,只开源部分代码,让你无法了解整个项目的架构设计。[国产开源项目对比](https://www.yuque.com/xiatian-bsgny/lm0ec1/wqf8mn) |
|||
|
|||
![开源项目对比](/.image/common/project-vs.png) |
|||
|
|||
③ 代码整洁、架构整洁,遵循《阿里巴巴 Java 开发手册》规范,代码注释详细,113770 行 Java 代码,42462 行代码注释。 |
|||
|
|||
## 🤝 项目外包 |
|||
|
|||
我们也是接外包滴,如果你有项目想要外包,可以微信联系【**Aix9975**】。 |
|||
|
|||
团队包含专业的项目经理、架构师、前端工程师、后端工程师、测试工程师、运维工程师,可以提供全流程的外包服务。 |
|||
|
|||
项目可以是商城、SCRM 系统、OA 系统、物流系统、ERP 系统、CMS 系统、HIS 系统、支付系统、IM 聊天、微信公众号、微信小程序等等。 |
|||
|
|||
## 🐼 内置功能 |
|||
|
|||
系统内置多种多种业务功能,可以用于快速你的业务系统: |
|||
|
|||
![功能分层](/.image/common/ruoyi-vue-pro-biz.png) |
|||
|
|||
* 通用模块(必选):系统功能、基础设施 |
|||
* 通用模块(可选):工作流程、支付系统、数据报表、会员中心 |
|||
* 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号、AI 大模型 |
|||
|
|||
> 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。 |
|||
> |
|||
> * 额外新增的功能,我们使用 🚀 标记。 |
|||
> * 重新实现的功能,我们使用 ⭐️ 标记。 |
|||
|
|||
🙂 所有功能,都通过 **单元测试** 保证高质量。 |
|||
|
|||
### 系统功能 |
|||
|
|||
| | 功能 | 描述 | |
|||
|-----|-------|---------------------------------| |
|||
| | 用户管理 | 用户是系统操作者,该功能主要完成系统用户配置 | |
|||
| ⭐️ | 在线用户 | 当前系统中活跃用户状态监控,支持手动踢下线 | |
|||
| | 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | |
|||
| | 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能 | |
|||
| | 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 | |
|||
| | 岗位管理 | 配置系统用户所属担任职务 | |
|||
| 🚀 | 租户管理 | 配置系统租户,支持 SaaS 场景下的多租户功能 | |
|||
| 🚀 | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 | |
|||
| | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | |
|||
| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 | |
|||
| 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 | |
|||
| 🚀 | 站内信 | 系统内的消息通知,提供站内信模版、站内信消息 | |
|||
| 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 | |
|||
| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 | |
|||
| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 | |
|||
| | 通知公告 | 系统通知公告信息发布维护 | |
|||
| 🚀 | 敏感词 | 配置系统敏感词,支持标签分组 | |
|||
| 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 | |
|||
| 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 | |
|||
|
|||
![功能图](/.image/common/system-feature.png) |
|||
|
|||
### 工作流程 |
|||
|
|||
| | 功能 | 描述 | |
|||
|-----|-------|----------------------------------------| |
|||
| 🚀 | 流程模型 | 配置工作流的流程模型,支持文件导入与在线设计流程图,提供 7 种任务分配规则 | |
|||
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 | |
|||
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 | |
|||
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 | |
|||
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转发、委派、退回等操作 | |
|||
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 | |
|||
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 | |
|||
|
|||
![功能图](/.image/common/bpm-feature.png) |
|||
|
|||
### 支付系统 |
|||
|
|||
| | 功能 | 描述 | |
|||
|-----|------|---------------------------| |
|||
| 🚀 | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 | |
|||
| 🚀 | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单 | |
|||
| 🚀 | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单 | |
|||
| 🚀 | 回调通知 | 查看支付回调业务的【支付】【退款】的通知结果 | |
|||
| 🚀 | 接入示例 | 提供接入支付系统的【支付】【退款】的功能实战 | |
|||
|
|||
### 基础设施 |
|||
|
|||
| | 功能 | 描述 | |
|||
|-----|-----------|----------------------------------------------| |
|||
| 🚀 | 代码生成 | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 | |
|||
| 🚀 | 系统接口 | 基于 Swagger 自动生成相关的 RESTful API 接口文档 | |
|||
| 🚀 | 数据库文档 | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式 | |
|||
| | 表单构建 | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件 | |
|||
| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 | |
|||
| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 | |
|||
| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 | |
|||
| 🚀 | WebSocket | 提供 WebSocket 接入示例,支持一对一、一对多发送方式 | |
|||
| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 | |
|||
| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 | |
|||
| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 | |
|||
| 🚀 | 消息队列 | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 | |
|||
| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 | |
|||
| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 | |
|||
| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 | |
|||
| 🚀 | 服务保障 | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景 | |
|||
| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 | |
|||
| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 | |
|||
|
|||
![功能图](/.image/common/infra-feature.png) |
|||
|
|||
### 数据报表 |
|||
|
|||
| | 功能 | 描述 | |
|||
|-----|-------|--------------------| |
|||
| 🚀 | 报表设计器 | 支持数据报表、图形报表、打印设计等 | |
|||
| 🚀 | 大屏设计器 | 拖拽生成数据大屏,内置几十种图表组件 | |
|||
|
|||
### 微信公众号 |
|||
|
|||
| | 功能 | 描述 | |
|||
|-----|--------|-------------------------------| |
|||
| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 | |
|||
| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 | |
|||
| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 | |
|||
| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 | |
|||
| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 | |
|||
| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 | |
|||
| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 | |
|||
| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 | |
|||
| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 | |
|||
| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 | |
|||
|
|||
### 商城系统 |
|||
|
|||
演示地址:<https://doc.iocoder.cn/mall-preview/> |
|||
|
|||
![功能图](/.image/common/mall-feature.png) |
|||
|
|||
![功能图](/.image/common/mall-preview.png) |
|||
|
|||
### 会员中心 |
|||
|
|||
| | 功能 | 描述 | |
|||
|-----|------|----------------------------------| |
|||
| 🚀 | 会员管理 | 会员是 C 端的消费者,该功能用于会员的搜索与管理 | |
|||
| 🚀 | 会员标签 | 对会员的标签进行创建、查询、修改、删除等操作 | |
|||
| 🚀 | 会员等级 | 对会员的等级、成长值进行管理,可用于订单折扣等会员权益 | |
|||
| 🚀 | 会员分组 | 对会员进行分组,用于用户画像、内容推送等运营手段 | |
|||
| 🚀 | 积分签到 | 回馈给签到、消费等行为的积分,会员可订单抵现、积分兑换等途径消耗 | |
|||
|
|||
### ERP 系统 |
|||
|
|||
演示地址:<https://doc.iocoder.cn/erp-preview/> |
|||
|
|||
![功能图](/.image/common/erp-feature.png) |
|||
|
|||
### CRM 系统 |
|||
|
|||
演示地址:<https://doc.iocoder.cn/crm-preview/> |
|||
|
|||
![功能图](/.image/common/crm-feature.png) |
|||
|
|||
### AI 大模型 |
|||
|
|||
演示地址:<https://doc.iocoder.cn/ai-preview/> |
|||
|
|||
![功能图](/.image/common/ai-feature.png) |
|||
|
|||
![功能图](/.image/common/ai-preview.gif) |
|||
|
|||
## 🐨 技术栈 |
|||
|
|||
### 模块 |
|||
|
|||
| 项目 | 说明 | |
|||
|-----------------------|--------------------| |
|||
| `win-dependencies` | Maven 依赖版本管理 | |
|||
| `win-framework` | Java 框架拓展 | |
|||
| `win-server` | 管理后台 + 用户 APP 的服务端 | |
|||
| `win-module-system` | 系统功能的 Module 模块 | |
|||
| `win-module-member` | 会员中心的 Module 模块 | |
|||
| `win-module-infra` | 基础设施的 Module 模块 | |
|||
| `win-module-bpm` | 工作流程的 Module 模块 | |
|||
| `win-module-pay` | 支付系统的 Module 模块 | |
|||
| `win-module-mall` | 商城系统的 Module 模块 | |
|||
| `win-module-erp` | ERP 系统的 Module 模块 | |
|||
| `win-module-crm` | CRM 系统的 Module 模块 | |
|||
| `win-module-ai` | AI 大模型的 Module 模块 | |
|||
| `win-module-mp` | 微信公众号的 Module 模块 | |
|||
| `win-module-report` | 大屏报表 Module 模块 | |
|||
|
|||
### 框架 |
|||
|
|||
| 框架 | 说明 | 版本 | 学习指南 | |
|||
|---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------| |
|||
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.3.4 | [文档](https://github.com/YunaiV/SpringBoot-Labs) | |
|||
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | | |
|||
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.23 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?win) | |
|||
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.7 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?win) | |
|||
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 4.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?win) | |
|||
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 /7.0 | | |
|||
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.32.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?win) | |
|||
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 6.1.10 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?win) | |
|||
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 6.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?win) | |
|||
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 8.0.1 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?win) | |
|||
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](https://doc.iocoder.cn/bpm/) | |
|||
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?win) | |
|||
| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.3.0 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?win) | |
|||
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.0.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?win) | |
|||
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?win) | |
|||
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.17.1 | | |
|||
| [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.6.2 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?win) | |
|||
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.34 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?win) | |
|||
| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.10.1 | - | |
|||
| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 5.7.0 | - | |
|||
|
|||
## 🐷 演示图 |
|||
|
|||
### 系统功能 |
|||
|
|||
| 模块 | biu | biu | biu | |
|||
|----------|-----------------------------|---------------------------|--------------------------| |
|||
| 登录 & 首页 | ![登录](/.image/登录.jpg) | ![首页](/.image/首页.jpg) | ![个人中心](/.image/个人中心.jpg) | |
|||
| 用户 & 应用 | ![用户管理](/.image/用户管理.jpg) | ![令牌管理](/.image/令牌管理.jpg) | ![应用管理](/.image/应用管理.jpg) | |
|||
| 租户 & 套餐 | ![租户管理](/.image/租户管理.jpg) | ![租户套餐](/.image/租户套餐.png) | - | |
|||
| 部门 & 岗位 | ![部门管理](/.image/部门管理.jpg) | ![岗位管理](/.image/岗位管理.jpg) | - | |
|||
| 菜单 & 角色 | ![菜单管理](/.image/菜单管理.jpg) | ![角色管理](/.image/角色管理.jpg) | - | |
|||
| 审计日志 | ![操作日志](/.image/操作日志.jpg) | ![登录日志](/.image/登录日志.jpg) | - | |
|||
| 短信 | ![短信渠道](/.image/短信渠道.jpg) | ![短信模板](/.image/短信模板.jpg) | ![短信日志](/.image/短信日志.jpg) | |
|||
| 字典 & 敏感词 | ![字典类型](/.image/字典类型.jpg) | ![字典数据](/.image/字典数据.jpg) | ![敏感词](/.image/敏感词.jpg) | |
|||
| 错误码 & 通知 | ![错误码管理](/.image/错误码管理.jpg) | ![通知公告](/.image/通知公告.jpg) | - | |
|||
|
|||
### 工作流程 |
|||
|
|||
| 模块 | biu | biu | biu | |
|||
|---------|---------------------------------|---------------------------------|---------------------------------| |
|||
| 流程模型 | ![流程模型-列表](/.image/流程模型-列表.jpg) | ![流程模型-设计](/.image/流程模型-设计.jpg) | ![流程模型-定义](/.image/流程模型-定义.jpg) | |
|||
| 表单 & 分组 | ![流程表单](/.image/流程表单.jpg) | ![用户分组](/.image/用户分组.jpg) | - | |
|||
| 我的流程 | ![我的流程-列表](/.image/我的流程-列表.jpg) | ![我的流程-发起](/.image/我的流程-发起.jpg) | ![我的流程-详情](/.image/我的流程-详情.jpg) | |
|||
| 待办 & 已办 | ![任务列表-审批](/.image/任务列表-审批.jpg) | ![任务列表-待办](/.image/任务列表-待办.jpg) | ![任务列表-已办](/.image/任务列表-已办.jpg) | |
|||
| OA 请假 | ![OA请假-列表](/.image/OA请假-列表.jpg) | ![OA请假-发起](/.image/OA请假-发起.jpg) | ![OA请假-详情](/.image/OA请假-详情.jpg) | |
|||
|
|||
### 基础设施 |
|||
|
|||
| 模块 | biu | biu | biu | |
|||
|---------------|-------------------------------|-----------------------------|---------------------------| |
|||
| 代码生成 | ![代码生成](/.image/代码生成.jpg) | ![生成效果](/.image/生成效果.jpg) | - | |
|||
| 文档 | ![系统接口](/.image/系统接口.jpg) | ![数据库文档](/.image/数据库文档.jpg) | - | |
|||
| 文件 & 配置 | ![文件配置](/.image/文件配置.jpg) | ![文件管理](/.image/文件管理2.jpg) | ![配置管理](/.image/配置管理.jpg) | |
|||
| 定时任务 | ![定时任务](/.image/定时任务.jpg) | ![任务日志](/.image/任务日志.jpg) | - | |
|||
| API 日志 | ![访问日志](/.image/访问日志.jpg) | ![错误日志](/.image/错误日志.jpg) | - | |
|||
| MySQL & Redis | ![MySQL](/.image/MySQL.jpg) | ![Redis](/.image/Redis.jpg) | - | |
|||
| 监控平台 | ![Java监控](/.image/Java监控.jpg) | ![链路追踪](/.image/链路追踪.jpg) | ![日志中心](/.image/日志中心.jpg) | |
|||
|
|||
### 支付系统 |
|||
|
|||
| 模块 | biu | biu | biu | |
|||
|---------|---------------------------|---------------------------------|---------------------------------| |
|||
| 商家 & 应用 | ![商户信息](/.image/商户信息.jpg) | ![应用信息-列表](/.image/应用信息-列表.jpg) | ![应用信息-编辑](/.image/应用信息-编辑.jpg) | |
|||
| 支付 & 退款 | ![支付订单](/.image/支付订单.jpg) | ![退款订单](/.image/退款订单.jpg) | --- | |
|||
### 数据报表 |
|||
|
|||
| 模块 | biu | biu | biu | |
|||
|-------|---------------------------------|---------------------------------|---------------------------------------| |
|||
| 报表设计器 | ![数据报表](/.image/报表设计器-数据报表.jpg) | ![图形报表](/.image/报表设计器-图形报表.jpg) | ![报表设计器-打印设计](/.image/报表设计器-打印设计.jpg) | |
|||
| 大屏设计器 | ![大屏列表](/.image/大屏设计器-列表.jpg) | ![大屏预览](/.image/大屏设计器-预览.jpg) | ![大屏编辑](/.image/大屏设计器-编辑.jpg) | |
|||
|
|||
### 移动端(管理后台) |
|||
|
|||
| biu | biu | biu | |
|||
|----------------------------------|----------------------------------|----------------------------------| |
|||
| ![](/.image/admin-uniapp/01.png) | ![](/.image/admin-uniapp/02.png) | ![](/.image/admin-uniapp/03.png) | |
|||
| ![](/.image/admin-uniapp/04.png) | ![](/.image/admin-uniapp/05.png) | ![](/.image/admin-uniapp/06.png) | |
|||
| ![](/.image/admin-uniapp/07.png) | ![](/.image/admin-uniapp/08.png) | ![](/.image/admin-uniapp/09.png) | |
|||
|
|||
目前已经实现登录、我的、工作台、编辑资料、头像修改、密码修改、常见问题、关于我们等基础功能。 |
@ -0,0 +1,4 @@ |
|||
config.stopBubbling = true |
|||
lombok.tostring.callsuper=CALL |
|||
lombok.equalsandhashcode.callsuper=CALL |
|||
lombok.accessors.chain=true |
@ -0,0 +1,171 @@ |
|||
<?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> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win</artifactId> |
|||
<version>${revision}</version> |
|||
<packaging>pom</packaging> |
|||
<modules> |
|||
<module>win-dependencies</module> |
|||
<module>win-framework</module> |
|||
<!-- Server 主项目 --> |
|||
<module>win-server</module> |
|||
<!-- 各种 module 拓展 --> |
|||
<module>win-module-system</module> |
|||
<module>win-module-infra</module> |
|||
<!-- <module>win-module-member</module>--> |
|||
<!-- <module>win-module-bpm</module>--> |
|||
<!-- <module>win-module-report</module>--> |
|||
<!-- <module>win-module-mp</module>--> |
|||
<!-- <module>win-module-pay</module>--> |
|||
<!-- <module>win-module-mall</module>--> |
|||
<!-- <module>win-module-crm</module>--> |
|||
<!-- <module>win-module-erp</module>--> |
|||
<!-- <module>win-module-ai</module>--> |
|||
<!-- <module>win-module-iot</module>--> |
|||
</modules> |
|||
|
|||
<name>${project.artifactId}</name> |
|||
<description>芋道项目基础脚手架</description> |
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
|||
|
|||
<properties> |
|||
<revision>2.3.0-SNAPSHOT</revision> |
|||
<!-- Maven 相关 --> |
|||
<java.version>17</java.version> |
|||
<maven.compiler.source>${java.version}</maven.compiler.source> |
|||
<maven.compiler.target>${java.version}</maven.compiler.target> |
|||
<maven-surefire-plugin.version>3.2.2</maven-surefire-plugin.version> |
|||
<maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version> |
|||
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> |
|||
<!-- 看看咋放到 bom 里 --> |
|||
<lombok.version>1.18.34</lombok.version> |
|||
<spring.boot.version>3.3.4</spring.boot.version> |
|||
<mapstruct.version>1.6.2</mapstruct.version> |
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
|||
</properties> |
|||
|
|||
<dependencyManagement> |
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-dependencies</artifactId> |
|||
<version>${revision}</version> |
|||
<type>pom</type> |
|||
<scope>import</scope> |
|||
</dependency> |
|||
</dependencies> |
|||
</dependencyManagement> |
|||
|
|||
<build> |
|||
<pluginManagement> |
|||
<plugins> |
|||
<!-- maven-surefire-plugin 插件,用于运行单元测试。 --> |
|||
<!-- 注意,需要使用 3.0.X+,因为要支持 Junit 5 版本 --> |
|||
<plugin> |
|||
<groupId>org.apache.maven.plugins</groupId> |
|||
<artifactId>maven-surefire-plugin</artifactId> |
|||
<version>${maven-surefire-plugin.version}</version> |
|||
</plugin> |
|||
<!-- maven-compiler-plugin 插件,解决 spring-boot-configuration-processor + Lombok + MapStruct 组合 --> |
|||
<!-- https://stackoverflow.com/questions/33483697/re-run-spring-boot-configuration-annotation-processor-to-update-generated-metada --> |
|||
<plugin> |
|||
<groupId>org.apache.maven.plugins</groupId> |
|||
<artifactId>maven-compiler-plugin</artifactId> |
|||
<version>${maven-compiler-plugin.version}</version> |
|||
<configuration> |
|||
<annotationProcessorPaths> |
|||
<path> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-configuration-processor</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
</path> |
|||
<path> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
<version>${lombok.version}</version> |
|||
</path> |
|||
<path> |
|||
<groupId>org.mapstruct</groupId> |
|||
<artifactId>mapstruct-processor</artifactId> |
|||
<version>${mapstruct.version}</version> |
|||
</path> |
|||
</annotationProcessorPaths> |
|||
<!-- 编译参数写在 arg 内,解决 Spring Boot 3.2 的 Parameter Name Discovery 问题 --> |
|||
<debug>false</debug> |
|||
<compilerArgs> |
|||
<arg>-parameters</arg> |
|||
</compilerArgs> |
|||
</configuration> |
|||
</plugin> |
|||
<plugin> |
|||
<groupId>org.codehaus.mojo</groupId> |
|||
<artifactId>flatten-maven-plugin</artifactId> |
|||
</plugin> |
|||
</plugins> |
|||
</pluginManagement> |
|||
|
|||
<plugins> |
|||
<!-- 统一 revision 版本 --> |
|||
<plugin> |
|||
<groupId>org.codehaus.mojo</groupId> |
|||
<artifactId>flatten-maven-plugin</artifactId> |
|||
<version>${flatten-maven-plugin.version}</version> |
|||
<configuration> |
|||
<flattenMode>oss</flattenMode> |
|||
<updatePomFile>true</updatePomFile> |
|||
</configuration> |
|||
<executions> |
|||
<execution> |
|||
<goals> |
|||
<goal>flatten</goal> |
|||
</goals> |
|||
<id>flatten</id> |
|||
<phase>process-resources</phase> |
|||
</execution> |
|||
<execution> |
|||
<goals> |
|||
<goal>clean</goal> |
|||
</goals> |
|||
<id>flatten.clean</id> |
|||
<phase>clean</phase> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
|
|||
<!-- 使用 huawei / aliyun 的 Maven 源,提升下载速度 --> |
|||
<repositories> |
|||
<repository> |
|||
<id>huaweicloud</id> |
|||
<name>huawei</name> |
|||
<url>https://mirrors.huaweicloud.com/repository/maven/</url> |
|||
</repository> |
|||
<repository> |
|||
<id>aliyunmaven</id> |
|||
<name>aliyun</name> |
|||
<url>https://maven.aliyun.com/repository/public</url> |
|||
</repository> |
|||
|
|||
<repository> |
|||
<id>spring-milestones</id> |
|||
<name>Spring Milestones</name> |
|||
<url>https://repo.spring.io/milestone</url> |
|||
<snapshots> |
|||
<enabled>false</enabled> |
|||
</snapshots> |
|||
</repository> |
|||
<repository> |
|||
<id>spring-snapshots</id> |
|||
<name>Spring Snapshots</name> |
|||
<url>https://repo.spring.io/snapshot</url> |
|||
<releases> |
|||
<enabled>false</enabled> |
|||
</releases> |
|||
</repository> |
|||
</repositories> |
|||
|
|||
</project> |
@ -0,0 +1,49 @@ |
|||
# Docker Build & Up |
|||
|
|||
目标: 快速部署体验系统,帮助了解系统之间的依赖关系。 |
|||
依赖:docker compose v2,删除`name: win-system`,降低`version`版本为`3.3`以下,支持`docker-compose`。 |
|||
|
|||
## 功能文件列表 |
|||
|
|||
```text |
|||
. |
|||
├── Docker-HOWTO.md |
|||
├── docker-compose.yml |
|||
├── docker.env <-- 提供docker-compose环境变量配置 |
|||
├── win-server |
|||
│ └── Dockerfile |
|||
└── win-ui-admin |
|||
├── .dockerignore |
|||
├── Dockerfile |
|||
└── nginx.conf <-- 提供基础配置,gzip压缩、api转发 |
|||
``` |
|||
|
|||
## 构建 jar 包 |
|||
|
|||
```shell |
|||
# 创建maven缓存volume |
|||
docker volume create --name win-maven-repo |
|||
|
|||
docker run -it --rm --name win-maven \ |
|||
-v win-maven-repo:/root/.m2 \ |
|||
-v $PWD:/usr/src/mymaven \ |
|||
-w /usr/src/mymaven \ |
|||
maven mvn clean install package '-Dmaven.test.skip=true' |
|||
``` |
|||
|
|||
## 构建启动服务 |
|||
|
|||
```shell |
|||
docker compose --env-file docker.env up -d |
|||
``` |
|||
|
|||
首次运行会自动构建容器。可以通过`docker compose build [service]`来手动构建所有或某个docker镜像 |
|||
|
|||
`--env-file docker.env`为可选参数,只是展示了通过`.env`文件配置容器启动的环境变量,`docker-compose.yml`本身已经提供足够的默认参数来正常运行系统。 |
|||
|
|||
## 服务器的宿主机端口映射 |
|||
|
|||
- admin ui: http://localhost:8080 |
|||
- api server: http://localhost:48080 |
|||
- mysql: root/123456, port: 3306 |
|||
- redis: port: 6379 |
@ -0,0 +1,84 @@ |
|||
version: "3.4" |
|||
|
|||
name: win-system |
|||
|
|||
services: |
|||
mysql: |
|||
container_name: win-mysql |
|||
image: mysql:8 |
|||
restart: unless-stopped |
|||
tty: true |
|||
ports: |
|||
- "3306:3306" |
|||
environment: |
|||
MYSQL_DATABASE: ${MYSQL_DATABASE:-ruoyi-vue-pro} |
|||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-123456} |
|||
volumes: |
|||
- mysql:/var/lib/mysql/ |
|||
- ./sql/mysql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/ruoyi-vue-pro.sql:ro |
|||
|
|||
redis: |
|||
container_name: win-redis |
|||
image: redis:6-alpine |
|||
restart: unless-stopped |
|||
ports: |
|||
- "6379:6379" |
|||
volumes: |
|||
- redis:/data |
|||
|
|||
server: |
|||
container_name: win-server |
|||
build: |
|||
context: ./win-server/ |
|||
image: win-server |
|||
restart: unless-stopped |
|||
ports: |
|||
- "48080:48080" |
|||
environment: |
|||
# https://github.com/polovyivan/docker-pass-configs-to-container |
|||
SPRING_PROFILES_ACTIVE: local |
|||
JAVA_OPTS: |
|||
${JAVA_OPTS:- |
|||
-Xms512m |
|||
-Xmx512m |
|||
-Djava.security.egd=file:/dev/./urandom |
|||
} |
|||
ARGS: |
|||
--spring.datasource.dynamic.datasource.master.url=${MASTER_DATASOURCE_URL:-jdbc:mysql://win-mysql:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true} |
|||
--spring.datasource.dynamic.datasource.master.username=${MASTER_DATASOURCE_USERNAME:-root} |
|||
--spring.datasource.dynamic.datasource.master.password=${MASTER_DATASOURCE_PASSWORD:-123456} |
|||
--spring.datasource.dynamic.datasource.slave.url=${SLAVE_DATASOURCE_URL:-jdbc:mysql://win-mysql:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true} |
|||
--spring.datasource.dynamic.datasource.slave.username=${SLAVE_DATASOURCE_USERNAME:-root} |
|||
--spring.datasource.dynamic.datasource.slave.password=${SLAVE_DATASOURCE_PASSWORD:-123456} |
|||
--spring.data.redis.host=${REDIS_HOST:-win-redis} |
|||
depends_on: |
|||
- mysql |
|||
- redis |
|||
|
|||
admin: |
|||
container_name: win-admin |
|||
build: |
|||
context: ./win-ui-admin |
|||
args: |
|||
NODE_ENV: |
|||
ENV=${NODE_ENV:-production} |
|||
PUBLIC_PATH=${PUBLIC_PATH:-/} |
|||
VUE_APP_TITLE=${VUE_APP_TITLE:-闻荫管理系统} |
|||
VUE_APP_BASE_API=${VUE_APP_BASE_API:-/prod-api} |
|||
VUE_APP_APP_NAME=${VUE_APP_APP_NAME:-/} |
|||
VUE_APP_TENANT_ENABLE=${VUE_APP_TENANT_ENABLE:-true} |
|||
VUE_APP_CAPTCHA_ENABLE=${VUE_APP_CAPTCHA_ENABLE:-true} |
|||
VUE_APP_DOC_ENABLE=${VUE_APP_DOC_ENABLE:-true} |
|||
VUE_APP_BAIDU_CODE=${VUE_APP_BAIDU_CODE:-fadc1bd5db1a1d6f581df60a1807f8ab} |
|||
image: win-admin |
|||
restart: unless-stopped |
|||
ports: |
|||
- "8080:80" |
|||
depends_on: |
|||
- server |
|||
|
|||
volumes: |
|||
mysql: |
|||
driver: local |
|||
redis: |
|||
driver: local |
@ -0,0 +1,25 @@ |
|||
## mysql |
|||
MYSQL_DATABASE=ruoyi-vue-pro |
|||
MYSQL_ROOT_PASSWORD=123456 |
|||
|
|||
## server |
|||
JAVA_OPTS=-Xms512m -Xmx512m -Djava.security.egd=file:/dev/./urandom |
|||
|
|||
MASTER_DATASOURCE_URL=jdbc:mysql://win-mysql:3306/${MYSQL_DATABASE}?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true |
|||
MASTER_DATASOURCE_USERNAME=root |
|||
MASTER_DATASOURCE_PASSWORD=${MYSQL_ROOT_PASSWORD} |
|||
SLAVE_DATASOURCE_URL=${MASTER_DATASOURCE_URL} |
|||
SLAVE_DATASOURCE_USERNAME=${MASTER_DATASOURCE_USERNAME} |
|||
SLAVE_DATASOURCE_PASSWORD=${MASTER_DATASOURCE_PASSWORD} |
|||
REDIS_HOST=win-redis |
|||
|
|||
## admin |
|||
NODE_ENV=production |
|||
PUBLIC_PATH=/ |
|||
VUE_APP_TITLE=闻荫管理系统 |
|||
VUE_APP_BASE_API=/prod-api |
|||
VUE_APP_APP_NAME=/ |
|||
VUE_APP_TENANT_ENABLE=true |
|||
VUE_APP_CAPTCHA_ENABLE=true |
|||
VUE_APP_DOC_ENABLE=true |
|||
VUE_APP_BAIDU_CODE=fadc1bd5db1a1d6f581df60a1807f8ab |
@ -0,0 +1,20 @@ |
|||
{ |
|||
"local": { |
|||
"baseUrl": "http://127.0.0.1:48080/admin-api", |
|||
"token": "test1", |
|||
"adminTenentId": "1", |
|||
|
|||
"appApi": "http://127.0.0.1:48080/app-api", |
|||
"appToken": "test247", |
|||
"appTenentId": "1" |
|||
}, |
|||
"gateway": { |
|||
"baseUrl": "http://127.0.0.1:8888/admin-api", |
|||
"token": "test1", |
|||
"adminTenentId": "1", |
|||
|
|||
"appApi": "http://127.0.0.1:8888/app-api", |
|||
"appToken": "test1", |
|||
"appTenantId": "1" |
|||
} |
|||
} |
@ -0,0 +1,60 @@ |
|||
#!groovy |
|||
pipeline { |
|||
|
|||
agent any |
|||
|
|||
parameters { |
|||
string(name: 'TAG_NAME', defaultValue: '', description: '') |
|||
} |
|||
|
|||
environment { |
|||
// DockerHub 凭证 ID(登录您的 DockerHub) |
|||
DOCKER_CREDENTIAL_ID = 'dockerhub-id' |
|||
// GitHub 凭证 ID (推送 tag 到 GitHub 仓库) |
|||
GITHUB_CREDENTIAL_ID = 'github-id' |
|||
// kubeconfig 凭证 ID (访问接入正在运行的 Kubernetes 集群) |
|||
KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig' |
|||
// 镜像的推送 |
|||
REGISTRY = 'docker.io' |
|||
// DockerHub 账号名 |
|||
DOCKERHUB_NAMESPACE = 'docker_username' |
|||
// GitHub 账号名 |
|||
GITHUB_ACCOUNT = 'https://gitee.com/zhijiantianya/ruoyi-vue-pro' |
|||
// 应用名称 |
|||
APP_NAME = 'win-server' |
|||
// 应用部署路径 |
|||
APP_DEPLOY_BASE_DIR = '/media/pi/KINGTON/data/work/projects/' |
|||
} |
|||
|
|||
stages { |
|||
stage('检出') { |
|||
steps { |
|||
git url: "https://gitee.com/will-we/ruoyi-vue-pro.git", |
|||
branch: "devops" |
|||
} |
|||
} |
|||
|
|||
stage('构建') { |
|||
steps { |
|||
// TODO 解决多环境链接、密码不同配置临时方案 |
|||
sh 'if [ ! -d "' + "${env.HOME}" + '/resources" ];then\n' + |
|||
' echo "配置文件不存在无需修改"\n' + |
|||
'else\n' + |
|||
' cp -rf ' + "${env.HOME}" + '/resources/*.yaml ' + "${env.APP_NAME}" + '/src/main/resources\n' + |
|||
' echo "配置文件替换"\n' + |
|||
'fi' |
|||
sh 'mvn clean package -Dmaven.test.skip=true' |
|||
} |
|||
} |
|||
|
|||
stage('部署') { |
|||
steps { |
|||
sh 'cp -f ' + ' bin/deploy.sh ' + "${env.APP_DEPLOY_BASE_DIR}" + "${env.APP_NAME}" |
|||
sh 'cp -f ' + "${env.APP_NAME}" + '/target/*.jar ' + "${env.APP_DEPLOY_BASE_DIR}" + "${env.APP_NAME}" +'/build/' |
|||
archiveArtifacts "${env.APP_NAME}" + '/target/*.jar' |
|||
sh 'chmod +x ' + "${env.APP_DEPLOY_BASE_DIR}" + "${env.APP_NAME}" + '/deploy.sh' |
|||
sh 'bash ' + "${env.APP_DEPLOY_BASE_DIR}" + "${env.APP_NAME}" + '/deploy.sh' |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,160 @@ |
|||
#!/bin/bash |
|||
set -e |
|||
|
|||
DATE=$(date +%Y%m%d%H%M) |
|||
# 基础路径 |
|||
BASE_PATH=/work/projects/win-server |
|||
# 编译后 jar 的地址。部署时,Jenkins 会上传 jar 包到该目录下 |
|||
SOURCE_PATH=$BASE_PATH/build |
|||
# 服务名称。同时约定部署服务的 jar 包名字也为它。 |
|||
SERVER_NAME=win-server |
|||
# 环境 |
|||
PROFILES_ACTIVE=development |
|||
# 健康检查 URL |
|||
HEALTH_CHECK_URL=http://127.0.0.1:48080/actuator/health/ |
|||
|
|||
# heapError 存放路径 |
|||
HEAP_ERROR_PATH=$BASE_PATH/heapError |
|||
# JVM 参数 |
|||
JAVA_OPS="-Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$HEAP_ERROR_PATH" |
|||
|
|||
# SkyWalking Agent 配置 |
|||
#export SW_AGENT_NAME=$SERVER_NAME |
|||
#export SW_AGENT_COLLECTOR_BACKEND_SERVICES=192.168.0.84:11800 |
|||
#export SW_GRPC_LOG_SERVER_HOST=192.168.0.84 |
|||
#export SW_AGENT_TRACE_IGNORE_PATH="Redisson/PING,/actuator/**,/admin/**" |
|||
#export JAVA_AGENT=-javaagent:/work/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar |
|||
|
|||
# 备份 |
|||
function backup() { |
|||
# 如果不存在,则无需备份 |
|||
if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then |
|||
echo "[backup] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过备份" |
|||
# 如果存在,则备份到 backup 目录下,使用时间作为后缀 |
|||
else |
|||
echo "[backup] 开始备份 $SERVER_NAME ..." |
|||
cp $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar |
|||
echo "[backup] 备份 $SERVER_NAME 完成" |
|||
fi |
|||
} |
|||
|
|||
# 最新构建代码 移动到项目环境 |
|||
function transfer() { |
|||
echo "[transfer] 开始转移 $SERVER_NAME.jar" |
|||
|
|||
# 删除原 jar 包 |
|||
if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then |
|||
echo "[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过删除" |
|||
else |
|||
echo "[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成" |
|||
rm $BASE_PATH/$SERVER_NAME.jar |
|||
fi |
|||
|
|||
# 复制新 jar 包 |
|||
echo "[transfer] 从 $SOURCE_PATH 中获取 $SERVER_NAME.jar 并迁移至 $BASE_PATH ...." |
|||
cp $SOURCE_PATH/$SERVER_NAME.jar $BASE_PATH |
|||
|
|||
echo "[transfer] 转移 $SERVER_NAME.jar 完成" |
|||
} |
|||
|
|||
# 停止:优雅关闭之前已经启动的服务 |
|||
function stop() { |
|||
echo "[stop] 开始停止 $BASE_PATH/$SERVER_NAME" |
|||
PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}') |
|||
# 如果 Java 服务启动中,则进行关闭 |
|||
if [ -n "$PID" ]; then |
|||
# 正常关闭 |
|||
echo "[stop] $BASE_PATH/$SERVER_NAME 运行中,开始 kill [$PID]" |
|||
kill -15 $PID |
|||
# 等待最大 120 秒,直到关闭完成。 |
|||
for ((i = 0; i < 120; i++)) |
|||
do |
|||
sleep 1 |
|||
PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}') |
|||
if [ -n "$PID" ]; then |
|||
echo -e ".\c" |
|||
else |
|||
echo "[stop] 停止 $BASE_PATH/$SERVER_NAME 成功" |
|||
break |
|||
fi |
|||
done |
|||
|
|||
# 如果正常关闭失败,那么进行强制 kill -9 进行关闭 |
|||
if [ -n "$PID" ]; then |
|||
echo "[stop] $BASE_PATH/$SERVER_NAME 失败,强制 kill -9 $PID" |
|||
kill -9 $PID |
|||
fi |
|||
# 如果 Java 服务未启动,则无需关闭 |
|||
else |
|||
echo "[stop] $BASE_PATH/$SERVER_NAME 未启动,无需停止" |
|||
fi |
|||
} |
|||
|
|||
# 启动:启动后端项目 |
|||
function start() { |
|||
# 开启启动前,打印启动参数 |
|||
echo "[start] 开始启动 $BASE_PATH/$SERVER_NAME" |
|||
echo "[start] JAVA_OPS: $JAVA_OPS" |
|||
echo "[start] JAVA_AGENT: $JAVA_AGENT" |
|||
echo "[start] PROFILES: $PROFILES_ACTIVE" |
|||
|
|||
# 开始启动 |
|||
BUILD_ID=dontKillMe nohup java -server $JAVA_OPS $JAVA_AGENT -jar $BASE_PATH/$SERVER_NAME.jar --spring.profiles.active=$PROFILES_ACTIVE & |
|||
echo "[start] 启动 $BASE_PATH/$SERVER_NAME 完成" |
|||
} |
|||
|
|||
# 健康检查:自动判断后端项目是否正常启动 |
|||
function healthCheck() { |
|||
# 如果配置健康检查,则进行健康检查 |
|||
if [ -n "$HEALTH_CHECK_URL" ]; then |
|||
# 健康检查最大 120 秒,直到健康检查通过 |
|||
echo "[healthCheck] 开始通过 $HEALTH_CHECK_URL 地址,进行健康检查"; |
|||
for ((i = 0; i < 120; i++)) |
|||
do |
|||
# 请求健康检查地址,只获取状态码。 |
|||
result=`curl -I -m 10 -o /dev/null -s -w %{http_code} $HEALTH_CHECK_URL || echo "000"` |
|||
# 如果状态码为 200,则说明健康检查通过 |
|||
if [ "$result" == "200" ]; then |
|||
echo "[healthCheck] 健康检查通过"; |
|||
break |
|||
# 如果状态码非 200,则说明未通过。sleep 1 秒后,继续重试 |
|||
else |
|||
echo -e ".\c" |
|||
sleep 1 |
|||
fi |
|||
done |
|||
|
|||
# 健康检查未通过,则异常退出 shell 脚本,不继续部署。 |
|||
if [ ! "$result" == "200" ]; then |
|||
echo "[healthCheck] 健康检查不通过,可能部署失败。查看日志,自行判断是否启动成功"; |
|||
tail -n 10 nohup.out |
|||
exit 1; |
|||
# 健康检查通过,打印最后 10 行日志,可能部署的人想看下日志。 |
|||
else |
|||
tail -n 10 nohup.out |
|||
fi |
|||
# 如果未配置健康检查,则 sleep 120 秒,人工看日志是否部署成功。 |
|||
else |
|||
echo "[healthCheck] HEALTH_CHECK_URL 未配置,开始 sleep 120 秒"; |
|||
sleep 120 |
|||
echo "[healthCheck] sleep 120 秒完成,查看日志,自行判断是否启动成功"; |
|||
tail -n 50 nohup.out |
|||
fi |
|||
} |
|||
|
|||
# 部署 |
|||
function deploy() { |
|||
cd $BASE_PATH |
|||
# 备份原 jar |
|||
backup |
|||
# 停止 Java 服务 |
|||
stop |
|||
# 部署新 jar |
|||
transfer |
|||
# 启动 Java 服务 |
|||
start |
|||
# 健康检查 |
|||
healthCheck |
|||
} |
|||
|
|||
deploy |
@ -0,0 +1,3 @@ |
|||
暂未适配 IBM DB2 数据库,如果你有需要,可以微信联系 wangwenbin-server 一起建设。 |
|||
|
|||
你需要把表结构与数据导入到 DM 数据库,我来测试与适配代码。 |
@ -0,0 +1,179 @@ |
|||
-- |
|||
-- A hint submitted by a user: Oracle DB MUST be created as "shared" and the |
|||
-- job_queue_processes parameter must be greater than 2 |
|||
-- However, these settings are pretty much standard after any |
|||
-- Oracle install, so most users need not worry about this. |
|||
-- |
|||
-- Many other users (including the primary author of Quartz) have had success |
|||
-- running in dedicated mode, so only consider the above as a hint ;-) |
|||
-- |
|||
|
|||
drop table if exists qrtz_calendars; |
|||
drop table if exists qrtz_fired_triggers; |
|||
drop table if exists qrtz_blob_triggers; |
|||
drop table if exists qrtz_cron_triggers; |
|||
drop table if exists qrtz_simple_triggers; |
|||
drop table if exists qrtz_simprop_triggers; |
|||
drop table if exists qrtz_triggers; |
|||
drop table if exists qrtz_job_details; |
|||
drop table if exists qrtz_paused_trigger_grps; |
|||
drop table if exists qrtz_locks; |
|||
drop table if exists qrtz_scheduler_state; |
|||
|
|||
CREATE TABLE qrtz_job_details |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
JOB_NAME VARCHAR2(200) NOT NULL, |
|||
JOB_GROUP VARCHAR2(200) NOT NULL, |
|||
DESCRIPTION VARCHAR2(250) NULL, |
|||
JOB_CLASS_NAME VARCHAR2(250) NOT NULL, |
|||
IS_DURABLE VARCHAR2(1) NOT NULL, |
|||
IS_NONCONCURRENT VARCHAR2(1) NOT NULL, |
|||
IS_UPDATE_DATA VARCHAR2(1) NOT NULL, |
|||
REQUESTS_RECOVERY VARCHAR2(1) NOT NULL, |
|||
JOB_DATA BLOB NULL, |
|||
CONSTRAINT QRTZ_JOB_DETAILS_PK PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) |
|||
); |
|||
CREATE TABLE qrtz_triggers |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
TRIGGER_NAME VARCHAR2(200) NOT NULL, |
|||
TRIGGER_GROUP VARCHAR2(200) NOT NULL, |
|||
JOB_NAME VARCHAR2(200) NOT NULL, |
|||
JOB_GROUP VARCHAR2(200) NOT NULL, |
|||
DESCRIPTION VARCHAR2(250) NULL, |
|||
NEXT_FIRE_TIME NUMBER(19) NULL, |
|||
PREV_FIRE_TIME NUMBER(19) NULL, |
|||
PRIORITY NUMBER(13) NULL, |
|||
TRIGGER_STATE VARCHAR2(16) NOT NULL, |
|||
TRIGGER_TYPE VARCHAR2(8) NOT NULL, |
|||
START_TIME NUMBER(19) NOT NULL, |
|||
END_TIME NUMBER(19) NULL, |
|||
CALENDAR_NAME VARCHAR2(200) NULL, |
|||
MISFIRE_INSTR NUMBER(2) NULL, |
|||
JOB_DATA BLOB NULL, |
|||
CONSTRAINT QRTZ_TRIGGERS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), |
|||
CONSTRAINT QRTZ_TRIGGER_TO_JOBS_FK FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) |
|||
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) |
|||
); |
|||
CREATE TABLE qrtz_simple_triggers |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
TRIGGER_NAME VARCHAR2(200) NOT NULL, |
|||
TRIGGER_GROUP VARCHAR2(200) NOT NULL, |
|||
REPEAT_COUNT NUMBER(7) NOT NULL, |
|||
REPEAT_INTERVAL NUMBER(12) NOT NULL, |
|||
TIMES_TRIGGERED NUMBER(10) NOT NULL, |
|||
CONSTRAINT QRTZ_SIMPLE_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), |
|||
CONSTRAINT QRTZ_SIMPLE_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) |
|||
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) |
|||
); |
|||
CREATE TABLE qrtz_cron_triggers |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
TRIGGER_NAME VARCHAR2(200) NOT NULL, |
|||
TRIGGER_GROUP VARCHAR2(200) NOT NULL, |
|||
CRON_EXPRESSION VARCHAR2(120) NOT NULL, |
|||
TIME_ZONE_ID VARCHAR2(80), |
|||
CONSTRAINT QRTZ_CRON_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), |
|||
CONSTRAINT QRTZ_CRON_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) |
|||
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) |
|||
); |
|||
CREATE TABLE qrtz_simprop_triggers |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
TRIGGER_NAME VARCHAR2(200) NOT NULL, |
|||
TRIGGER_GROUP VARCHAR2(200) NOT NULL, |
|||
STR_PROP_1 VARCHAR2(512) NULL, |
|||
STR_PROP_2 VARCHAR2(512) NULL, |
|||
STR_PROP_3 VARCHAR2(512) NULL, |
|||
INT_PROP_1 NUMBER(10) NULL, |
|||
INT_PROP_2 NUMBER(10) NULL, |
|||
LONG_PROP_1 NUMBER(19) NULL, |
|||
LONG_PROP_2 NUMBER(19) NULL, |
|||
DEC_PROP_1 NUMERIC(13,4) NULL, |
|||
DEC_PROP_2 NUMERIC(13,4) NULL, |
|||
BOOL_PROP_1 VARCHAR2(1) NULL, |
|||
BOOL_PROP_2 VARCHAR2(1) NULL, |
|||
TIME_ZONE_ID VARCHAR2(80) NULL, |
|||
CONSTRAINT QRTZ_SIMPROP_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), |
|||
CONSTRAINT QRTZ_SIMPROP_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) |
|||
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) |
|||
); |
|||
CREATE TABLE qrtz_blob_triggers |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
TRIGGER_NAME VARCHAR2(200) NOT NULL, |
|||
TRIGGER_GROUP VARCHAR2(200) NOT NULL, |
|||
BLOB_DATA BLOB NULL, |
|||
CONSTRAINT QRTZ_BLOB_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), |
|||
CONSTRAINT QRTZ_BLOB_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) |
|||
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) |
|||
); |
|||
CREATE TABLE qrtz_calendars |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
CALENDAR_NAME VARCHAR2(200) NOT NULL, |
|||
CALENDAR BLOB NOT NULL, |
|||
CONSTRAINT QRTZ_CALENDARS_PK PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) |
|||
); |
|||
CREATE TABLE qrtz_paused_trigger_grps |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
TRIGGER_GROUP VARCHAR2(200) NOT NULL, |
|||
CONSTRAINT QRTZ_PAUSED_TRIG_GRPS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) |
|||
); |
|||
CREATE TABLE qrtz_fired_triggers |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
ENTRY_ID VARCHAR2(140) NOT NULL, |
|||
TRIGGER_NAME VARCHAR2(200) NOT NULL, |
|||
TRIGGER_GROUP VARCHAR2(200) NOT NULL, |
|||
INSTANCE_NAME VARCHAR2(200) NOT NULL, |
|||
FIRED_TIME NUMBER(19) NOT NULL, |
|||
SCHED_TIME NUMBER(19) NOT NULL, |
|||
PRIORITY NUMBER(13) NOT NULL, |
|||
STATE VARCHAR2(16) NOT NULL, |
|||
JOB_NAME VARCHAR2(200) NULL, |
|||
JOB_GROUP VARCHAR2(200) NULL, |
|||
IS_NONCONCURRENT VARCHAR2(1) NULL, |
|||
REQUESTS_RECOVERY VARCHAR2(1) NULL, |
|||
CONSTRAINT QRTZ_FIRED_TRIGGER_PK PRIMARY KEY (SCHED_NAME,ENTRY_ID) |
|||
); |
|||
CREATE TABLE qrtz_scheduler_state |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
INSTANCE_NAME VARCHAR2(200) NOT NULL, |
|||
LAST_CHECKIN_TIME NUMBER(19) NOT NULL, |
|||
CHECKIN_INTERVAL NUMBER(13) NOT NULL, |
|||
CONSTRAINT QRTZ_SCHEDULER_STATE_PK PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) |
|||
); |
|||
CREATE TABLE qrtz_locks |
|||
( |
|||
SCHED_NAME VARCHAR2(120) NOT NULL, |
|||
LOCK_NAME VARCHAR2(40) NOT NULL, |
|||
CONSTRAINT QRTZ_LOCKS_PK PRIMARY KEY (SCHED_NAME,LOCK_NAME) |
|||
); |
|||
|
|||
create index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY); |
|||
create index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP); |
|||
|
|||
create index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); |
|||
create index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP); |
|||
create index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME); |
|||
create index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP); |
|||
create index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE); |
|||
create index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); |
|||
create index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); |
|||
create index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME); |
|||
create index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); |
|||
create index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); |
|||
create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); |
|||
create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); |
|||
|
|||
create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME); |
|||
create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); |
|||
create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); |
|||
create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP); |
|||
create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); |
|||
create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP); |
File diff suppressed because it is too large
@ -0,0 +1,170 @@ |
|||
set client_min_messages = WARNING; |
|||
DROP TABLE IF EXISTS qrtz_fired_triggers; |
|||
DROP TABLE IF EXISTS qrtz_paused_trigger_grps; |
|||
DROP TABLE IF EXISTS qrtz_scheduler_state; |
|||
DROP TABLE IF EXISTS qrtz_locks; |
|||
DROP TABLE IF EXISTS qrtz_simprop_triggers; |
|||
DROP TABLE IF EXISTS qrtz_simple_triggers; |
|||
DROP TABLE IF EXISTS qrtz_cron_triggers; |
|||
DROP TABLE IF EXISTS qrtz_blob_triggers; |
|||
DROP TABLE IF EXISTS qrtz_triggers; |
|||
DROP TABLE IF EXISTS qrtz_job_details; |
|||
DROP TABLE IF EXISTS qrtz_calendars; |
|||
set client_min_messages = NOTICE; |
|||
|
|||
CREATE TABLE qrtz_job_details |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
job_name TEXT NOT NULL, |
|||
job_group TEXT NOT NULL, |
|||
description TEXT NULL, |
|||
job_class_name TEXT NOT NULL, |
|||
is_durable BOOL NOT NULL, |
|||
is_nonconcurrent BOOL NOT NULL, |
|||
is_update_data BOOL NOT NULL, |
|||
requests_recovery BOOL NOT NULL, |
|||
job_data BYTEA NULL, |
|||
PRIMARY KEY (sched_name,job_name,job_group) |
|||
); |
|||
|
|||
CREATE TABLE qrtz_triggers |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
trigger_name TEXT NOT NULL, |
|||
trigger_group TEXT NOT NULL, |
|||
job_name TEXT NOT NULL, |
|||
job_group TEXT NOT NULL, |
|||
description TEXT NULL, |
|||
next_fire_time BIGINT NULL, |
|||
prev_fire_time BIGINT NULL, |
|||
priority INTEGER NULL, |
|||
trigger_state TEXT NOT NULL, |
|||
trigger_type TEXT NOT NULL, |
|||
start_time BIGINT NOT NULL, |
|||
end_time BIGINT NULL, |
|||
calendar_name TEXT NULL, |
|||
misfire_instr SMALLINT NULL, |
|||
job_data BYTEA NULL, |
|||
PRIMARY KEY (sched_name,trigger_name,trigger_group), |
|||
FOREIGN KEY (sched_name,job_name,job_group) |
|||
REFERENCES qrtz_job_details(sched_name,job_name,job_group) |
|||
); |
|||
|
|||
CREATE TABLE qrtz_simple_triggers |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
trigger_name TEXT NOT NULL, |
|||
trigger_group TEXT NOT NULL, |
|||
repeat_count BIGINT NOT NULL, |
|||
repeat_interval BIGINT NOT NULL, |
|||
times_triggered BIGINT NOT NULL, |
|||
PRIMARY KEY (sched_name,trigger_name,trigger_group), |
|||
FOREIGN KEY (sched_name,trigger_name,trigger_group) |
|||
REFERENCES qrtz_triggers(sched_name,trigger_name,trigger_group) ON DELETE CASCADE |
|||
); |
|||
|
|||
CREATE TABLE QRTZ_SIMPROP_TRIGGERS |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
trigger_name TEXT NOT NULL , |
|||
trigger_group TEXT NOT NULL , |
|||
str_prop_1 TEXT NULL, |
|||
str_prop_2 TEXT NULL, |
|||
str_prop_3 TEXT NULL, |
|||
int_prop_1 INTEGER NULL, |
|||
int_prop_2 INTEGER NULL, |
|||
long_prop_1 BIGINT NULL, |
|||
long_prop_2 BIGINT NULL, |
|||
dec_prop_1 NUMERIC NULL, |
|||
dec_prop_2 NUMERIC NULL, |
|||
bool_prop_1 BOOL NULL, |
|||
bool_prop_2 BOOL NULL, |
|||
time_zone_id TEXT NULL, |
|||
PRIMARY KEY (sched_name,trigger_name,trigger_group), |
|||
FOREIGN KEY (sched_name,trigger_name,trigger_group) |
|||
REFERENCES qrtz_triggers(sched_name,trigger_name,trigger_group) ON DELETE CASCADE |
|||
); |
|||
|
|||
CREATE TABLE qrtz_cron_triggers |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
trigger_name TEXT NOT NULL, |
|||
trigger_group TEXT NOT NULL, |
|||
cron_expression TEXT NOT NULL, |
|||
time_zone_id TEXT, |
|||
PRIMARY KEY (sched_name,trigger_name,trigger_group), |
|||
FOREIGN KEY (sched_name,trigger_name,trigger_group) |
|||
REFERENCES qrtz_triggers(sched_name,trigger_name,trigger_group) ON DELETE CASCADE |
|||
); |
|||
|
|||
CREATE TABLE qrtz_blob_triggers |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
trigger_name TEXT NOT NULL, |
|||
trigger_group TEXT NOT NULL, |
|||
blob_data BYTEA NULL, |
|||
PRIMARY KEY (sched_name,trigger_name,trigger_group), |
|||
FOREIGN KEY (sched_name,trigger_name,trigger_group) |
|||
REFERENCES qrtz_triggers(sched_name,trigger_name,trigger_group) ON DELETE CASCADE |
|||
); |
|||
|
|||
CREATE TABLE qrtz_calendars |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
calendar_name TEXT NOT NULL, |
|||
calendar BYTEA NOT NULL, |
|||
PRIMARY KEY (sched_name,calendar_name) |
|||
); |
|||
|
|||
CREATE TABLE qrtz_paused_trigger_grps |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
trigger_group TEXT NOT NULL, |
|||
PRIMARY KEY (sched_name,trigger_group) |
|||
); |
|||
|
|||
CREATE TABLE qrtz_fired_triggers |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
entry_id TEXT NOT NULL, |
|||
trigger_name TEXT NOT NULL, |
|||
trigger_group TEXT NOT NULL, |
|||
instance_name TEXT NOT NULL, |
|||
fired_time BIGINT NOT NULL, |
|||
sched_time BIGINT NOT NULL, |
|||
priority INTEGER NOT NULL, |
|||
state TEXT NOT NULL, |
|||
job_name TEXT NULL, |
|||
job_group TEXT NULL, |
|||
is_nonconcurrent BOOL NOT NULL, |
|||
requests_recovery BOOL NULL, |
|||
PRIMARY KEY (sched_name,entry_id) |
|||
); |
|||
|
|||
CREATE TABLE qrtz_scheduler_state |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
instance_name TEXT NOT NULL, |
|||
last_checkin_time BIGINT NOT NULL, |
|||
checkin_interval BIGINT NOT NULL, |
|||
PRIMARY KEY (sched_name,instance_name) |
|||
); |
|||
|
|||
CREATE TABLE qrtz_locks |
|||
( |
|||
sched_name TEXT NOT NULL, |
|||
lock_name TEXT NOT NULL, |
|||
PRIMARY KEY (sched_name,lock_name) |
|||
); |
|||
|
|||
create index idx_qrtz_j_req_recovery on qrtz_job_details(requests_recovery); |
|||
create index idx_qrtz_t_next_fire_time on qrtz_triggers(next_fire_time); |
|||
create index idx_qrtz_t_state on qrtz_triggers(trigger_state); |
|||
create index idx_qrtz_t_nft_st on qrtz_triggers(next_fire_time,trigger_state); |
|||
create index idx_qrtz_ft_trig_name on qrtz_fired_triggers(trigger_name); |
|||
create index idx_qrtz_ft_trig_group on qrtz_fired_triggers(trigger_group); |
|||
create index idx_qrtz_ft_trig_nm_gp on qrtz_fired_triggers(sched_name,trigger_name,trigger_group); |
|||
create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(instance_name); |
|||
create index idx_qrtz_ft_job_name on qrtz_fired_triggers(job_name); |
|||
create index idx_qrtz_ft_job_group on qrtz_fired_triggers(job_group); |
|||
create index idx_qrtz_ft_job_req_recovery on qrtz_fired_triggers(requests_recovery); |
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
@ -0,0 +1,253 @@ |
|||
-- ---------------------------- |
|||
-- qrtz_blob_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_blob_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
blob_data bytea NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
CREATE INDEX idx_qrtz_blob_triggers_sched_name ON qrtz_blob_triggers (sched_name, trigger_name, trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_calendars |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_calendars |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
calendar_name varchar(190) NOT NULL, |
|||
calendar bytea NOT NULL, |
|||
PRIMARY KEY (sched_name, calendar_name) |
|||
); |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_cron_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_cron_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
cron_expression varchar(120) NOT NULL, |
|||
time_zone_id varchar(80) NULL DEFAULT NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_fired_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_fired_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
entry_id varchar(95) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
instance_name varchar(190) NOT NULL, |
|||
fired_time int8 NOT NULL, |
|||
sched_time int8 NOT NULL, |
|||
priority int4 NOT NULL, |
|||
state varchar(16) NOT NULL, |
|||
job_name varchar(190) NULL DEFAULT NULL, |
|||
job_group varchar(190) NULL DEFAULT NULL, |
|||
is_nonconcurrent varchar(1) NULL DEFAULT NULL, |
|||
requests_recovery varchar(1) NULL DEFAULT NULL, |
|||
PRIMARY KEY (sched_name, entry_id) |
|||
); |
|||
|
|||
CREATE INDEX idx_qrtz_ft_trig_inst_name ON qrtz_fired_triggers (sched_name, instance_name); |
|||
CREATE INDEX idx_qrtz_ft_inst_job_req_rcvry ON qrtz_fired_triggers (sched_name, instance_name, requests_recovery); |
|||
CREATE INDEX idx_qrtz_ft_j_g ON qrtz_fired_triggers (sched_name, job_name, job_group); |
|||
CREATE INDEX idx_qrtz_ft_jg ON qrtz_fired_triggers (sched_name, job_group); |
|||
CREATE INDEX idx_qrtz_ft_t_g ON qrtz_fired_triggers (sched_name, trigger_name, trigger_group); |
|||
CREATE INDEX idx_qrtz_ft_tg ON qrtz_fired_triggers (sched_name, trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_job_details |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_job_details |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
job_name varchar(190) NOT NULL, |
|||
job_group varchar(190) NOT NULL, |
|||
description varchar(250) NULL DEFAULT NULL, |
|||
job_class_name varchar(250) NOT NULL, |
|||
is_durable varchar(1) NOT NULL, |
|||
is_nonconcurrent varchar(1) NOT NULL, |
|||
is_update_data varchar(1) NOT NULL, |
|||
requests_recovery varchar(1) NOT NULL, |
|||
job_data bytea NULL, |
|||
PRIMARY KEY (sched_name, job_name, job_group) |
|||
); |
|||
|
|||
CREATE INDEX idx_qrtz_j_req_recovery ON qrtz_job_details (sched_name, requests_recovery); |
|||
CREATE INDEX idx_qrtz_j_grp ON qrtz_job_details (sched_name, job_group); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_locks |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_locks |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
lock_name varchar(40) NOT NULL, |
|||
PRIMARY KEY (sched_name, lock_name) |
|||
); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_paused_trigger_grps |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_paused_trigger_grps |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
PRIMARY KEY (sched_name, trigger_group) |
|||
); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_scheduler_state |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_scheduler_state |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
instance_name varchar(190) NOT NULL, |
|||
last_checkin_time int8 NOT NULL, |
|||
checkin_interval int8 NOT NULL, |
|||
PRIMARY KEY (sched_name, instance_name) |
|||
); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_simple_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_simple_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
repeat_count int8 NOT NULL, |
|||
repeat_interval int8 NOT NULL, |
|||
times_triggered int8 NOT NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_simprop_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_simprop_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
str_prop_1 varchar(512) NULL DEFAULT NULL, |
|||
str_prop_2 varchar(512) NULL DEFAULT NULL, |
|||
str_prop_3 varchar(512) NULL DEFAULT NULL, |
|||
int_prop_1 int4 NULL DEFAULT NULL, |
|||
int_prop_2 int4 NULL DEFAULT NULL, |
|||
long_prop_1 int8 NULL DEFAULT NULL, |
|||
long_prop_2 int8 NULL DEFAULT NULL, |
|||
dec_prop_1 numeric(13, 4) NULL DEFAULT NULL, |
|||
dec_prop_2 numeric(13, 4) NULL DEFAULT NULL, |
|||
bool_prop_1 varchar(1) NULL DEFAULT NULL, |
|||
bool_prop_2 varchar(1) NULL DEFAULT NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
job_name varchar(190) NOT NULL, |
|||
job_group varchar(190) NOT NULL, |
|||
description varchar(250) NULL DEFAULT NULL, |
|||
next_fire_time int8 NULL DEFAULT NULL, |
|||
prev_fire_time int8 NULL DEFAULT NULL, |
|||
priority int4 NULL DEFAULT NULL, |
|||
trigger_state varchar(16) NOT NULL, |
|||
trigger_type varchar(8) NOT NULL, |
|||
start_time int8 NOT NULL, |
|||
end_time int8 NULL DEFAULT NULL, |
|||
calendar_name varchar(190) NULL DEFAULT NULL, |
|||
misfire_instr int2 NULL DEFAULT NULL, |
|||
job_data bytea NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
CREATE INDEX idx_qrtz_t_j ON qrtz_triggers (sched_name, job_name, job_group); |
|||
CREATE INDEX idx_qrtz_t_jg ON qrtz_triggers (sched_name, job_group); |
|||
CREATE INDEX idx_qrtz_t_c ON qrtz_triggers (sched_name, calendar_name); |
|||
CREATE INDEX idx_qrtz_t_g ON qrtz_triggers (sched_name, trigger_group); |
|||
CREATE INDEX idx_qrtz_t_state ON qrtz_triggers (sched_name, trigger_state); |
|||
CREATE INDEX idx_qrtz_t_n_state ON qrtz_triggers (sched_name, trigger_name, trigger_group, trigger_state); |
|||
CREATE INDEX idx_qrtz_t_n_g_state ON qrtz_triggers (sched_name, trigger_group, trigger_state); |
|||
CREATE INDEX idx_qrtz_t_next_fire_time ON qrtz_triggers (sched_name, next_fire_time); |
|||
CREATE INDEX idx_qrtz_t_nft_st ON qrtz_triggers (sched_name, trigger_state, next_fire_time); |
|||
CREATE INDEX idx_qrtz_t_nft_misfire ON qrtz_triggers (sched_name, misfire_instr, next_fire_time); |
|||
CREATE INDEX idx_qrtz_t_nft_st_misfire ON qrtz_triggers (sched_name, misfire_instr, next_fire_time, trigger_state); |
|||
CREATE INDEX idx_qrtz_t_nft_st_misfire_grp ON qrtz_triggers (sched_name, misfire_instr, next_fire_time, trigger_group, |
|||
trigger_state); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_blob_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_blob_triggers |
|||
ADD CONSTRAINT qrtz_blob_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, |
|||
trigger_name, |
|||
trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_cron_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_cron_triggers |
|||
ADD CONSTRAINT qrtz_cron_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, trigger_name, trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_simple_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_simple_triggers |
|||
ADD CONSTRAINT qrtz_simple_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, |
|||
trigger_name, |
|||
trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_simprop_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_simprop_triggers |
|||
ADD CONSTRAINT qrtz_simprop_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, trigger_name, trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_triggers |
|||
ADD CONSTRAINT qrtz_triggers_ibfk_1 FOREIGN KEY (sched_name, job_name, job_group) REFERENCES qrtz_job_details (sched_name, job_name, job_group); |
File diff suppressed because it is too large
@ -0,0 +1,845 @@ |
|||
/* |
|||
注意:仅仅需要 Quartz 定时任务的场景,可选!!! |
|||
|
|||
Date: 15/06/2022 08:20:08 |
|||
*/ |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_BLOB_TRIGGERS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_BLOB_TRIGGERS"; |
|||
CREATE TABLE "QRTZ_BLOB_TRIGGERS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"TRIGGER_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"TRIGGER_GROUP" VARCHAR2(200 BYTE) NOT NULL, |
|||
"BLOB_DATA" BLOB |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_BLOB_TRIGGERS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_CALENDARS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_CALENDARS"; |
|||
CREATE TABLE "QRTZ_CALENDARS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"CALENDAR_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"CALENDAR" BLOB NOT NULL |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_CALENDARS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_CRON_TRIGGERS"; |
|||
CREATE TABLE "QRTZ_CRON_TRIGGERS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"TRIGGER_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"TRIGGER_GROUP" VARCHAR2(200 BYTE) NOT NULL, |
|||
"CRON_EXPRESSION" VARCHAR2(120 BYTE) NOT NULL, |
|||
"TIME_ZONE_ID" VARCHAR2(80 BYTE) |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_FIRED_TRIGGERS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_FIRED_TRIGGERS"; |
|||
CREATE TABLE "QRTZ_FIRED_TRIGGERS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"ENTRY_ID" VARCHAR2(95 BYTE) NOT NULL, |
|||
"TRIGGER_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"TRIGGER_GROUP" VARCHAR2(200 BYTE) NOT NULL, |
|||
"INSTANCE_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"FIRED_TIME" NUMBER(13,0) NOT NULL, |
|||
"SCHED_TIME" NUMBER(13,0) NOT NULL, |
|||
"PRIORITY" NUMBER(13,0) NOT NULL, |
|||
"STATE" VARCHAR2(16 BYTE) NOT NULL, |
|||
"JOB_NAME" VARCHAR2(200 BYTE), |
|||
"JOB_GROUP" VARCHAR2(200 BYTE), |
|||
"IS_NONCONCURRENT" VARCHAR2(1 BYTE), |
|||
"REQUESTS_RECOVERY" VARCHAR2(1 BYTE) |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_FIRED_TRIGGERS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_JOB_DETAILS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_JOB_DETAILS"; |
|||
CREATE TABLE "QRTZ_JOB_DETAILS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"JOB_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"JOB_GROUP" VARCHAR2(200 BYTE) NOT NULL, |
|||
"DESCRIPTION" VARCHAR2(250 BYTE), |
|||
"JOB_CLASS_NAME" VARCHAR2(250 BYTE) NOT NULL, |
|||
"IS_DURABLE" VARCHAR2(1 BYTE) NOT NULL, |
|||
"IS_NONCONCURRENT" VARCHAR2(1 BYTE) NOT NULL, |
|||
"IS_UPDATE_DATA" VARCHAR2(1 BYTE) NOT NULL, |
|||
"REQUESTS_RECOVERY" VARCHAR2(1 BYTE) NOT NULL, |
|||
"JOB_DATA" BLOB |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_JOB_DETAILS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_LOCKS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_LOCKS"; |
|||
CREATE TABLE "QRTZ_LOCKS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"LOCK_NAME" VARCHAR2(40 BYTE) NOT NULL |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_LOCKS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_PAUSED_TRIGGER_GRPS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_PAUSED_TRIGGER_GRPS"; |
|||
CREATE TABLE "QRTZ_PAUSED_TRIGGER_GRPS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"TRIGGER_GROUP" VARCHAR2(200 BYTE) NOT NULL |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_PAUSED_TRIGGER_GRPS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_SCHEDULER_STATE |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_SCHEDULER_STATE"; |
|||
CREATE TABLE "QRTZ_SCHEDULER_STATE" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"INSTANCE_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"LAST_CHECKIN_TIME" NUMBER(13,0) NOT NULL, |
|||
"CHECKIN_INTERVAL" NUMBER(13,0) NOT NULL |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_SCHEDULER_STATE |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_SIMPLE_TRIGGERS"; |
|||
CREATE TABLE "QRTZ_SIMPLE_TRIGGERS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"TRIGGER_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"TRIGGER_GROUP" VARCHAR2(200 BYTE) NOT NULL, |
|||
"REPEAT_COUNT" NUMBER(7,0) NOT NULL, |
|||
"REPEAT_INTERVAL" NUMBER(12,0) NOT NULL, |
|||
"TIMES_TRIGGERED" NUMBER(10,0) NOT NULL |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_SIMPROP_TRIGGERS"; |
|||
CREATE TABLE "QRTZ_SIMPROP_TRIGGERS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"TRIGGER_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"TRIGGER_GROUP" VARCHAR2(200 BYTE) NOT NULL, |
|||
"STR_PROP_1" VARCHAR2(512 BYTE), |
|||
"STR_PROP_2" VARCHAR2(512 BYTE), |
|||
"STR_PROP_3" VARCHAR2(512 BYTE), |
|||
"INT_PROP_1" NUMBER(10,0), |
|||
"INT_PROP_2" NUMBER(10,0), |
|||
"LONG_PROP_1" NUMBER(13,0), |
|||
"LONG_PROP_2" NUMBER(13,0), |
|||
"DEC_PROP_1" NUMBER(13,4), |
|||
"DEC_PROP_2" NUMBER(13,4), |
|||
"BOOL_PROP_1" VARCHAR2(1 BYTE), |
|||
"BOOL_PROP_2" VARCHAR2(1 BYTE) |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
DROP TABLE "QRTZ_TRIGGERS"; |
|||
CREATE TABLE "QRTZ_TRIGGERS" ( |
|||
"SCHED_NAME" VARCHAR2(120 BYTE) NOT NULL, |
|||
"TRIGGER_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"TRIGGER_GROUP" VARCHAR2(200 BYTE) NOT NULL, |
|||
"JOB_NAME" VARCHAR2(200 BYTE) NOT NULL, |
|||
"JOB_GROUP" VARCHAR2(200 BYTE) NOT NULL, |
|||
"DESCRIPTION" VARCHAR2(250 BYTE), |
|||
"NEXT_FIRE_TIME" NUMBER(13,0), |
|||
"PREV_FIRE_TIME" NUMBER(13,0), |
|||
"PRIORITY" NUMBER(13,0), |
|||
"TRIGGER_STATE" VARCHAR2(16 BYTE) NOT NULL, |
|||
"TRIGGER_TYPE" VARCHAR2(8 BYTE) NOT NULL, |
|||
"START_TIME" NUMBER(13,0) NOT NULL, |
|||
"END_TIME" NUMBER(13,0), |
|||
"CALENDAR_NAME" VARCHAR2(200 BYTE), |
|||
"MISFIRE_INSTR" NUMBER(2,0), |
|||
"JOB_DATA" BLOB |
|||
) |
|||
LOGGING |
|||
NOCOMPRESS |
|||
PCTFREE 10 |
|||
INITRANS 1 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
) |
|||
PARALLEL 1 |
|||
NOCACHE |
|||
DISABLE ROW MOVEMENT |
|||
; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
COMMIT; |
|||
COMMIT; |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_BLOB_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_BLOB_TRIGGERS" ADD CONSTRAINT "QRTZ_BLOB_TRIG_PK" PRIMARY KEY ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_BLOB_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_BLOB_TRIGGERS" ADD CONSTRAINT "SYS_C008266" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_BLOB_TRIGGERS" ADD CONSTRAINT "SYS_C008267" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_BLOB_TRIGGERS" ADD CONSTRAINT "SYS_C008268" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_BLOB_TRIGGERS" ADD CONSTRAINT "SYS_C008653" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_BLOB_TRIGGERS" ADD CONSTRAINT "SYS_C008654" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_BLOB_TRIGGERS" ADD CONSTRAINT "SYS_C008655" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_CALENDARS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_CALENDARS" ADD CONSTRAINT "QRTZ_CALENDARS_PK" PRIMARY KEY ("SCHED_NAME", "CALENDAR_NAME"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_CALENDARS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_CALENDARS" ADD CONSTRAINT "SYS_C008271" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CALENDARS" ADD CONSTRAINT "SYS_C008272" CHECK ("CALENDAR_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CALENDARS" ADD CONSTRAINT "SYS_C008273" CHECK ("CALENDAR" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CALENDARS" ADD CONSTRAINT "SYS_C008656" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CALENDARS" ADD CONSTRAINT "SYS_C008657" CHECK ("CALENDAR_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CALENDARS" ADD CONSTRAINT "SYS_C008658" CHECK ("CALENDAR" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "QRTZ_CRON_TRIG_PK" PRIMARY KEY ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "SYS_C008255" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "SYS_C008256" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "SYS_C008257" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "SYS_C008258" CHECK ("CRON_EXPRESSION" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "SYS_C008659" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "SYS_C008660" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "SYS_C008661" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "SYS_C008662" CHECK ("CRON_EXPRESSION" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_FIRED_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "QRTZ_FIRED_TRIGGER_PK" PRIMARY KEY ("SCHED_NAME", "ENTRY_ID"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_FIRED_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008278" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008279" CHECK ("ENTRY_ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008280" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008281" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008282" CHECK ("INSTANCE_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008283" CHECK ("FIRED_TIME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008284" CHECK ("SCHED_TIME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008285" CHECK ("PRIORITY" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008286" CHECK ("STATE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008663" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008664" CHECK ("ENTRY_ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008665" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008666" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008667" CHECK ("INSTANCE_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008668" CHECK ("FIRED_TIME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008669" CHECK ("SCHED_TIME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008670" CHECK ("PRIORITY" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_FIRED_TRIGGERS" ADD CONSTRAINT "SYS_C008671" CHECK ("STATE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Indexes structure for table QRTZ_FIRED_TRIGGERS |
|||
-- ---------------------------- |
|||
CREATE INDEX "IDX_QRTZ_FT_INST_JOB_REQ_RCVRY" |
|||
ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "INSTANCE_NAME" ASC, "REQUESTS_RECOVERY" ASC) |
|||
LOGGING |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_FT_JG" |
|||
ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "JOB_GROUP" ASC) |
|||
LOGGING |
|||
ONLINE |
|||
NOSORT |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_FT_J_G" |
|||
ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "JOB_NAME" ASC, "JOB_GROUP" ASC) |
|||
LOGGING |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_FT_TG" |
|||
ON "QRTZ_FIRED_TRIGGERS" ("SCHED_NAME" ASC, "TRIGGER_GROUP" ASC) LOCAL |
|||
LOGGING |
|||
NOSORT |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_JOB_DETAILS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_JOB_DETAILS" ADD CONSTRAINT "QRTZ_JOB_DETAILS_PK" PRIMARY KEY ("SCHED_NAME", "JOB_NAME", "JOB_GROUP"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_JOB_DETAILS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_JOB_DETAILS" ADD CONSTRAINT "SYS_C008228" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_JOB_DETAILS" ADD CONSTRAINT "SYS_C008229" CHECK ("JOB_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_JOB_DETAILS" ADD CONSTRAINT "SYS_C008230" CHECK ("JOB_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_JOB_DETAILS" ADD CONSTRAINT "SYS_C008231" CHECK ("JOB_CLASS_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_JOB_DETAILS" ADD CONSTRAINT "SYS_C008232" CHECK ("IS_DURABLE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_JOB_DETAILS" ADD CONSTRAINT "SYS_C008233" CHECK ("IS_NONCONCURRENT" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_JOB_DETAILS" ADD CONSTRAINT "SYS_C008234" CHECK ("IS_UPDATE_DATA" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_JOB_DETAILS" ADD CONSTRAINT "SYS_C008235" CHECK ("REQUESTS_RECOVERY" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Indexes structure for table QRTZ_JOB_DETAILS |
|||
-- ---------------------------- |
|||
CREATE INDEX "IDX_QRTZ_J_GRP" |
|||
ON "QRTZ_JOB_DETAILS" ("SCHED_NAME" ASC, "JOB_GROUP" ASC) |
|||
LOGGING |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_J_REQ_RECOVERY" |
|||
ON "QRTZ_JOB_DETAILS" ("SCHED_NAME" ASC, "REQUESTS_RECOVERY" ASC) LOCAL |
|||
LOGGING |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_LOCKS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_LOCKS" ADD CONSTRAINT "QRTZ_LOCKS_PK" PRIMARY KEY ("SCHED_NAME", "LOCK_NAME"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_LOCKS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_LOCKS" ADD CONSTRAINT "SYS_C008293" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_LOCKS" ADD CONSTRAINT "SYS_C008294" CHECK ("LOCK_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_LOCKS" ADD CONSTRAINT "SYS_C008672" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_LOCKS" ADD CONSTRAINT "SYS_C008673" CHECK ("LOCK_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_PAUSED_TRIGGER_GRPS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_PAUSED_TRIGGER_GRPS" ADD CONSTRAINT "QRTZ_PAUSED_TRIG_GRPS_PK" PRIMARY KEY ("SCHED_NAME", "TRIGGER_GROUP"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_PAUSED_TRIGGER_GRPS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_PAUSED_TRIGGER_GRPS" ADD CONSTRAINT "SYS_C008275" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_PAUSED_TRIGGER_GRPS" ADD CONSTRAINT "SYS_C008276" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_PAUSED_TRIGGER_GRPS" ADD CONSTRAINT "SYS_C008674" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_PAUSED_TRIGGER_GRPS" ADD CONSTRAINT "SYS_C008675" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_SCHEDULER_STATE |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_SCHEDULER_STATE" ADD CONSTRAINT "QRTZ_SCHEDULER_STATE_PK" PRIMARY KEY ("SCHED_NAME", "INSTANCE_NAME"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_SCHEDULER_STATE |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_SCHEDULER_STATE" ADD CONSTRAINT "SYS_C008288" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SCHEDULER_STATE" ADD CONSTRAINT "SYS_C008289" CHECK ("INSTANCE_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SCHEDULER_STATE" ADD CONSTRAINT "SYS_C008290" CHECK ("LAST_CHECKIN_TIME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SCHEDULER_STATE" ADD CONSTRAINT "SYS_C008291" CHECK ("CHECKIN_INTERVAL" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SCHEDULER_STATE" ADD CONSTRAINT "SYS_C008676" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SCHEDULER_STATE" ADD CONSTRAINT "SYS_C008677" CHECK ("INSTANCE_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SCHEDULER_STATE" ADD CONSTRAINT "SYS_C008678" CHECK ("LAST_CHECKIN_TIME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SCHEDULER_STATE" ADD CONSTRAINT "SYS_C008679" CHECK ("CHECKIN_INTERVAL" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "QRTZ_SIMPLE_TRIG_PK" PRIMARY KEY ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008247" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008248" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008249" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008250" CHECK ("REPEAT_COUNT" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008251" CHECK ("REPEAT_INTERVAL" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008252" CHECK ("TIMES_TRIGGERED" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008680" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008681" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008682" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008683" CHECK ("REPEAT_COUNT" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008684" CHECK ("REPEAT_INTERVAL" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "SYS_C008685" CHECK ("TIMES_TRIGGERED" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_SIMPROP_TRIGGERS" ADD CONSTRAINT "QRTZ_SIMPROP_TRIG_PK" PRIMARY KEY ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_SIMPROP_TRIGGERS" ADD CONSTRAINT "SYS_C008261" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPROP_TRIGGERS" ADD CONSTRAINT "SYS_C008262" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPROP_TRIGGERS" ADD CONSTRAINT "SYS_C008263" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPROP_TRIGGERS" ADD CONSTRAINT "SYS_C008686" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPROP_TRIGGERS" ADD CONSTRAINT "SYS_C008687" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_SIMPROP_TRIGGERS" ADD CONSTRAINT "SYS_C008688" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "QRTZ_TRIGGERS_PK" PRIMARY KEY ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP"); |
|||
|
|||
-- ---------------------------- |
|||
-- Checks structure for table QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008237" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008238" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008239" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008240" CHECK ("JOB_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008241" CHECK ("JOB_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008242" CHECK ("TRIGGER_STATE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008243" CHECK ("TRIGGER_TYPE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008244" CHECK ("START_TIME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008689" CHECK ("SCHED_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008690" CHECK ("TRIGGER_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008691" CHECK ("TRIGGER_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008692" CHECK ("JOB_NAME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008693" CHECK ("JOB_GROUP" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008694" CHECK ("TRIGGER_STATE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008695" CHECK ("TRIGGER_TYPE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
ALTER TABLE "QRTZ_TRIGGERS" ADD CONSTRAINT "SYS_C008696" CHECK ("START_TIME" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Indexes structure for table QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
CREATE INDEX "IDX_QRTZ_T_C" |
|||
ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "CALENDAR_NAME" ASC) LOCAL |
|||
LOGGING |
|||
ONLINE |
|||
NOSORT |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_T_J" |
|||
ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "JOB_NAME" ASC, "JOB_GROUP" ASC) |
|||
LOGGING |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_T_JG" |
|||
ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "JOB_GROUP" ASC) LOCAL |
|||
LOGGING |
|||
ONLINE |
|||
NOSORT |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_T_NEXT_FIRE_TIME" |
|||
ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "NEXT_FIRE_TIME" ASC) |
|||
LOGGING |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_T_NFT_ST" |
|||
ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "TRIGGER_STATE" ASC, "NEXT_FIRE_TIME" ASC) LOCAL |
|||
LOGGING |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_T_NFT_ST_MISFIRE" |
|||
ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "MISFIRE_INSTR" ASC, "NEXT_FIRE_TIME" ASC, "TRIGGER_STATE" ASC) LOCAL |
|||
LOGGING |
|||
ONLINE |
|||
NOSORT |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
CREATE INDEX "IDX_QRTZ_T_STATE" |
|||
ON "QRTZ_TRIGGERS" ("SCHED_NAME" ASC, "TRIGGER_STATE" ASC) |
|||
LOGGING |
|||
VISIBLE |
|||
PCTFREE 10 |
|||
INITRANS 2 |
|||
STORAGE ( |
|||
INITIAL 65536 |
|||
NEXT 1048576 |
|||
MINEXTENTS 1 |
|||
MAXEXTENTS 2147483645 |
|||
FREELISTS 1 |
|||
FREELIST GROUPS 1 |
|||
BUFFER_POOL DEFAULT |
|||
); |
|||
|
|||
-- ---------------------------- |
|||
-- Foreign Keys structure for table QRTZ_BLOB_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_BLOB_TRIGGERS" ADD CONSTRAINT "QRTZ_BLOB_TRIG_TO_TRIG_FK" FOREIGN KEY ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP") REFERENCES "QRTZ_TRIGGERS" ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Foreign Keys structure for table QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_CRON_TRIGGERS" ADD CONSTRAINT "QRTZ_CRON_TRIG_TO_TRIG_FK" FOREIGN KEY ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP") REFERENCES "QRTZ_TRIGGERS" ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Foreign Keys structure for table QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_SIMPLE_TRIGGERS" ADD CONSTRAINT "QRTZ_SIMPLE_TRIG_TO_TRIG_FK" FOREIGN KEY ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP") REFERENCES "QRTZ_TRIGGERS" ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
|||
|
|||
-- ---------------------------- |
|||
-- Foreign Keys structure for table QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE "QRTZ_SIMPROP_TRIGGERS" ADD CONSTRAINT "QRTZ_SIMPROP_TRIG_TO_TRIG_FK" FOREIGN KEY ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP") REFERENCES "QRTZ_TRIGGERS" ("SCHED_NAME", "TRIGGER_NAME", "TRIGGER_GROUP") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE; |
File diff suppressed because it is too large
@ -0,0 +1,253 @@ |
|||
-- ---------------------------- |
|||
-- qrtz_blob_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_blob_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
blob_data bytea NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
CREATE INDEX idx_qrtz_blob_triggers_sched_name ON qrtz_blob_triggers (sched_name, trigger_name, trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_calendars |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_calendars |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
calendar_name varchar(190) NOT NULL, |
|||
calendar bytea NOT NULL, |
|||
PRIMARY KEY (sched_name, calendar_name) |
|||
); |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_cron_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_cron_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
cron_expression varchar(120) NOT NULL, |
|||
time_zone_id varchar(80) NULL DEFAULT NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_fired_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_fired_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
entry_id varchar(95) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
instance_name varchar(190) NOT NULL, |
|||
fired_time int8 NOT NULL, |
|||
sched_time int8 NOT NULL, |
|||
priority int4 NOT NULL, |
|||
state varchar(16) NOT NULL, |
|||
job_name varchar(190) NULL DEFAULT NULL, |
|||
job_group varchar(190) NULL DEFAULT NULL, |
|||
is_nonconcurrent varchar(1) NULL DEFAULT NULL, |
|||
requests_recovery varchar(1) NULL DEFAULT NULL, |
|||
PRIMARY KEY (sched_name, entry_id) |
|||
); |
|||
|
|||
CREATE INDEX idx_qrtz_ft_trig_inst_name ON qrtz_fired_triggers (sched_name, instance_name); |
|||
CREATE INDEX idx_qrtz_ft_inst_job_req_rcvry ON qrtz_fired_triggers (sched_name, instance_name, requests_recovery); |
|||
CREATE INDEX idx_qrtz_ft_j_g ON qrtz_fired_triggers (sched_name, job_name, job_group); |
|||
CREATE INDEX idx_qrtz_ft_jg ON qrtz_fired_triggers (sched_name, job_group); |
|||
CREATE INDEX idx_qrtz_ft_t_g ON qrtz_fired_triggers (sched_name, trigger_name, trigger_group); |
|||
CREATE INDEX idx_qrtz_ft_tg ON qrtz_fired_triggers (sched_name, trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_job_details |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_job_details |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
job_name varchar(190) NOT NULL, |
|||
job_group varchar(190) NOT NULL, |
|||
description varchar(250) NULL DEFAULT NULL, |
|||
job_class_name varchar(250) NOT NULL, |
|||
is_durable varchar(1) NOT NULL, |
|||
is_nonconcurrent varchar(1) NOT NULL, |
|||
is_update_data varchar(1) NOT NULL, |
|||
requests_recovery varchar(1) NOT NULL, |
|||
job_data bytea NULL, |
|||
PRIMARY KEY (sched_name, job_name, job_group) |
|||
); |
|||
|
|||
CREATE INDEX idx_qrtz_j_req_recovery ON qrtz_job_details (sched_name, requests_recovery); |
|||
CREATE INDEX idx_qrtz_j_grp ON qrtz_job_details (sched_name, job_group); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_locks |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_locks |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
lock_name varchar(40) NOT NULL, |
|||
PRIMARY KEY (sched_name, lock_name) |
|||
); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_paused_trigger_grps |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_paused_trigger_grps |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
PRIMARY KEY (sched_name, trigger_group) |
|||
); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_scheduler_state |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_scheduler_state |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
instance_name varchar(190) NOT NULL, |
|||
last_checkin_time int8 NOT NULL, |
|||
checkin_interval int8 NOT NULL, |
|||
PRIMARY KEY (sched_name, instance_name) |
|||
); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_simple_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_simple_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
repeat_count int8 NOT NULL, |
|||
repeat_interval int8 NOT NULL, |
|||
times_triggered int8 NOT NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_simprop_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_simprop_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
str_prop_1 varchar(512) NULL DEFAULT NULL, |
|||
str_prop_2 varchar(512) NULL DEFAULT NULL, |
|||
str_prop_3 varchar(512) NULL DEFAULT NULL, |
|||
int_prop_1 int4 NULL DEFAULT NULL, |
|||
int_prop_2 int4 NULL DEFAULT NULL, |
|||
long_prop_1 int8 NULL DEFAULT NULL, |
|||
long_prop_2 int8 NULL DEFAULT NULL, |
|||
dec_prop_1 numeric(13, 4) NULL DEFAULT NULL, |
|||
dec_prop_2 numeric(13, 4) NULL DEFAULT NULL, |
|||
bool_prop_1 varchar(1) NULL DEFAULT NULL, |
|||
bool_prop_2 varchar(1) NULL DEFAULT NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
-- ---------------------------- |
|||
-- qrtz_triggers |
|||
-- ---------------------------- |
|||
CREATE TABLE qrtz_triggers |
|||
( |
|||
sched_name varchar(120) NOT NULL, |
|||
trigger_name varchar(190) NOT NULL, |
|||
trigger_group varchar(190) NOT NULL, |
|||
job_name varchar(190) NOT NULL, |
|||
job_group varchar(190) NOT NULL, |
|||
description varchar(250) NULL DEFAULT NULL, |
|||
next_fire_time int8 NULL DEFAULT NULL, |
|||
prev_fire_time int8 NULL DEFAULT NULL, |
|||
priority int4 NULL DEFAULT NULL, |
|||
trigger_state varchar(16) NOT NULL, |
|||
trigger_type varchar(8) NOT NULL, |
|||
start_time int8 NOT NULL, |
|||
end_time int8 NULL DEFAULT NULL, |
|||
calendar_name varchar(190) NULL DEFAULT NULL, |
|||
misfire_instr int2 NULL DEFAULT NULL, |
|||
job_data bytea NULL, |
|||
PRIMARY KEY (sched_name, trigger_name, trigger_group) |
|||
); |
|||
|
|||
CREATE INDEX idx_qrtz_t_j ON qrtz_triggers (sched_name, job_name, job_group); |
|||
CREATE INDEX idx_qrtz_t_jg ON qrtz_triggers (sched_name, job_group); |
|||
CREATE INDEX idx_qrtz_t_c ON qrtz_triggers (sched_name, calendar_name); |
|||
CREATE INDEX idx_qrtz_t_g ON qrtz_triggers (sched_name, trigger_group); |
|||
CREATE INDEX idx_qrtz_t_state ON qrtz_triggers (sched_name, trigger_state); |
|||
CREATE INDEX idx_qrtz_t_n_state ON qrtz_triggers (sched_name, trigger_name, trigger_group, trigger_state); |
|||
CREATE INDEX idx_qrtz_t_n_g_state ON qrtz_triggers (sched_name, trigger_group, trigger_state); |
|||
CREATE INDEX idx_qrtz_t_next_fire_time ON qrtz_triggers (sched_name, next_fire_time); |
|||
CREATE INDEX idx_qrtz_t_nft_st ON qrtz_triggers (sched_name, trigger_state, next_fire_time); |
|||
CREATE INDEX idx_qrtz_t_nft_misfire ON qrtz_triggers (sched_name, misfire_instr, next_fire_time); |
|||
CREATE INDEX idx_qrtz_t_nft_st_misfire ON qrtz_triggers (sched_name, misfire_instr, next_fire_time, trigger_state); |
|||
CREATE INDEX idx_qrtz_t_nft_st_misfire_grp ON qrtz_triggers (sched_name, misfire_instr, next_fire_time, trigger_group, |
|||
trigger_state); |
|||
|
|||
-- @formatter:off |
|||
BEGIN; |
|||
COMMIT; |
|||
-- @formatter:on |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_blob_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_blob_triggers |
|||
ADD CONSTRAINT qrtz_blob_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, |
|||
trigger_name, |
|||
trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_cron_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_cron_triggers |
|||
ADD CONSTRAINT qrtz_cron_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, trigger_name, trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_simple_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_simple_triggers |
|||
ADD CONSTRAINT qrtz_simple_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, |
|||
trigger_name, |
|||
trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_simprop_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_simprop_triggers |
|||
ADD CONSTRAINT qrtz_simprop_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, trigger_name, trigger_group); |
|||
|
|||
-- ---------------------------- |
|||
-- FK: qrtz_triggers |
|||
-- ---------------------------- |
|||
ALTER TABLE qrtz_triggers |
|||
ADD CONSTRAINT qrtz_triggers_ibfk_1 FOREIGN KEY (sched_name, job_name, job_group) REFERENCES qrtz_job_details (sched_name, job_name, job_group); |
File diff suppressed because it is too large
@ -0,0 +1,533 @@ |
|||
/* |
|||
注意:仅仅需要 Quartz 定时任务的场景,可选!!! |
|||
|
|||
Date: 30/04/2024 09:54:18 |
|||
*/ |
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_BLOB_TRIGGERS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_BLOB_TRIGGERS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_BLOB_TRIGGERS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_BLOB_TRIGGERS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[BLOB_DATA] varbinary(max) NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_BLOB_TRIGGERS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_BLOB_TRIGGERS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_CALENDARS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_CALENDARS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_CALENDARS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_CALENDARS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[CALENDAR_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[CALENDAR] varbinary(max) NOT NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_CALENDARS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_CALENDARS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_CRON_TRIGGERS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_CRON_TRIGGERS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_CRON_TRIGGERS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[CRON_EXPRESSION] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TIME_ZONE_ID] varchar(80) COLLATE SQL_Latin1_General_CP1_CI_AS NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_FIRED_TRIGGERS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_FIRED_TRIGGERS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_FIRED_TRIGGERS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_FIRED_TRIGGERS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[ENTRY_ID] varchar(95) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[INSTANCE_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[FIRED_TIME] bigint NOT NULL, |
|||
[SCHED_TIME] bigint NOT NULL, |
|||
[PRIORITY] int NOT NULL, |
|||
[STATE] varchar(16) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[JOB_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[JOB_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[IS_NONCONCURRENT] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[REQUESTS_RECOVERY] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_FIRED_TRIGGERS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_FIRED_TRIGGERS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_JOB_DETAILS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_JOB_DETAILS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_JOB_DETAILS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_JOB_DETAILS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[JOB_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[JOB_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[DESCRIPTION] varchar(250) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[JOB_CLASS_NAME] varchar(250) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[IS_DURABLE] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[IS_NONCONCURRENT] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[IS_UPDATE_DATA] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[REQUESTS_RECOVERY] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[JOB_DATA] varbinary(max) NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_JOB_DETAILS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_JOB_DETAILS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_LOCKS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_LOCKS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_LOCKS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_LOCKS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[LOCK_NAME] varchar(40) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_LOCKS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_LOCKS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_PAUSED_TRIGGER_GRPS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_PAUSED_TRIGGER_GRPS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_PAUSED_TRIGGER_GRPS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_SCHEDULER_STATE |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_SCHEDULER_STATE]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_SCHEDULER_STATE] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_SCHEDULER_STATE] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[INSTANCE_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[LAST_CHECKIN_TIME] bigint NOT NULL, |
|||
[CHECKIN_INTERVAL] bigint NOT NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_SCHEDULER_STATE] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_SCHEDULER_STATE |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_SIMPLE_TRIGGERS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[REPEAT_COUNT] bigint NOT NULL, |
|||
[REPEAT_INTERVAL] bigint NOT NULL, |
|||
[TIMES_TRIGGERED] bigint NOT NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_SIMPROP_TRIGGERS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[STR_PROP_1] varchar(512) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[STR_PROP_2] varchar(512) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[STR_PROP_3] varchar(512) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[INT_PROP_1] int NULL, |
|||
[INT_PROP_2] int NULL, |
|||
[LONG_PROP_1] bigint NULL, |
|||
[LONG_PROP_2] bigint NULL, |
|||
[DEC_PROP_1] numeric(13,4) NULL, |
|||
[DEC_PROP_2] numeric(13,4) NULL, |
|||
[BOOL_PROP_1] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[BOOL_PROP_2] varchar(1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Table structure for QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
IF EXISTS (SELECT * FROM sys.all_objects WHERE object_id = OBJECT_ID(N'[dbo].[QRTZ_TRIGGERS]') AND type IN ('U')) |
|||
DROP TABLE [dbo].[QRTZ_TRIGGERS] |
|||
GO |
|||
|
|||
CREATE TABLE [dbo].[QRTZ_TRIGGERS] ( |
|||
[SCHED_NAME] varchar(120) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[JOB_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[JOB_GROUP] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[DESCRIPTION] varchar(250) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[NEXT_FIRE_TIME] bigint NULL, |
|||
[PREV_FIRE_TIME] bigint NULL, |
|||
[PRIORITY] int NULL, |
|||
[TRIGGER_STATE] varchar(16) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[TRIGGER_TYPE] varchar(8) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, |
|||
[START_TIME] bigint NOT NULL, |
|||
[END_TIME] bigint NULL, |
|||
[CALENDAR_NAME] varchar(200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, |
|||
[MISFIRE_INSTR] smallint NULL, |
|||
[JOB_DATA] varbinary(max) NULL |
|||
) |
|||
GO |
|||
|
|||
ALTER TABLE [dbo].[QRTZ_TRIGGERS] SET (LOCK_ESCALATION = TABLE) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Records of QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
BEGIN TRANSACTION |
|||
GO |
|||
|
|||
COMMIT |
|||
GO |
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_CALENDARS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_CALENDARS] ADD CONSTRAINT [PK_QRTZ_CALENDARS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [CALENDAR_NAME]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Indexes structure for table QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
CREATE NONCLUSTERED INDEX [IX_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS] |
|||
ON [dbo].[QRTZ_CRON_TRIGGERS] ( |
|||
[SCHED_NAME] ASC, |
|||
[TRIGGER_NAME] ASC, |
|||
[TRIGGER_GROUP] ASC |
|||
) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_CRON_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_FIRED_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_FIRED_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_FIRED_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [ENTRY_ID]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_JOB_DETAILS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_JOB_DETAILS] ADD CONSTRAINT [PK_QRTZ_JOB_DETAILS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [JOB_NAME], [JOB_GROUP]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_LOCKS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_LOCKS] ADD CONSTRAINT [PK_QRTZ_LOCKS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [LOCK_NAME]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_PAUSED_TRIGGER_GRPS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] ADD CONSTRAINT [PK_QRTZ_PAUSED_TRIGGER_GRPS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_GROUP]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_SCHEDULER_STATE |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_SCHEDULER_STATE] ADD CONSTRAINT [PK_QRTZ_SCHEDULER_STATE] PRIMARY KEY CLUSTERED ([SCHED_NAME], [INSTANCE_NAME]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Indexes structure for table QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
CREATE NONCLUSTERED INDEX [IX_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS] |
|||
ON [dbo].[QRTZ_SIMPLE_TRIGGERS] ( |
|||
[SCHED_NAME] ASC, |
|||
[TRIGGER_NAME] ASC, |
|||
[TRIGGER_GROUP] ASC |
|||
) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_SIMPLE_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Indexes structure for table QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
CREATE NONCLUSTERED INDEX [IX_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS] |
|||
ON [dbo].[QRTZ_SIMPROP_TRIGGERS] ( |
|||
[SCHED_NAME] ASC, |
|||
[TRIGGER_NAME] ASC, |
|||
[TRIGGER_GROUP] ASC |
|||
) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_SIMPROP_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Indexes structure for table QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
CREATE NONCLUSTERED INDEX [IX_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS] |
|||
ON [dbo].[QRTZ_TRIGGERS] ( |
|||
[SCHED_NAME] ASC, |
|||
[TRIGGER_NAME] ASC, |
|||
[TRIGGER_GROUP] ASC |
|||
) |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Primary Key structure for table QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_TRIGGERS] ADD CONSTRAINT [PK_QRTZ_TRIGGERS] PRIMARY KEY CLUSTERED ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) |
|||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) |
|||
ON [PRIMARY] |
|||
GO |
|||
|
|||
-- ---------------------------- |
|||
-- Foreign Keys structure for table QRTZ_BLOB_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_BLOB_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_BLOB_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) REFERENCES [dbo].[QRTZ_TRIGGERS] ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) ON DELETE CASCADE ON UPDATE NO ACTION |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Foreign Keys structure for table QRTZ_CRON_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) REFERENCES [dbo].[QRTZ_TRIGGERS] ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) ON DELETE CASCADE ON UPDATE NO ACTION |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Foreign Keys structure for table QRTZ_SIMPLE_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) REFERENCES [dbo].[QRTZ_TRIGGERS] ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) ON DELETE CASCADE ON UPDATE NO ACTION |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Foreign Keys structure for table QRTZ_SIMPROP_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) REFERENCES [dbo].[QRTZ_TRIGGERS] ([SCHED_NAME], [TRIGGER_NAME], [TRIGGER_GROUP]) ON DELETE CASCADE ON UPDATE NO ACTION |
|||
GO |
|||
|
|||
|
|||
-- ---------------------------- |
|||
-- Foreign Keys structure for table QRTZ_TRIGGERS |
|||
-- ---------------------------- |
|||
ALTER TABLE [dbo].[QRTZ_TRIGGERS] ADD CONSTRAINT [FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS] FOREIGN KEY ([SCHED_NAME], [JOB_NAME], [JOB_GROUP]) REFERENCES [dbo].[QRTZ_JOB_DETAILS] ([SCHED_NAME], [JOB_NAME], [JOB_GROUP]) ON DELETE NO ACTION ON UPDATE NO ACTION |
|||
GO |
File diff suppressed because it is too large
@ -0,0 +1,8 @@ |
|||
# 忽略python虚拟环境 |
|||
.env |
|||
.venv |
|||
env/ |
|||
venv/ |
|||
ENV/ |
|||
env.bak/ |
|||
venv.bak/ |
@ -0,0 +1,130 @@ |
|||
## 0. 友情提示 |
|||
|
|||
在 `sql/tools` 目录下,我们提供一些数据库相关的工具,包括测试数据库的快速启动、MySQL 转换其它数据库等等。 |
|||
|
|||
注意!所有的操作,必须在 `sql/tools` 目录下执行。 |
|||
|
|||
## 1. 测试数据库的快速启动 |
|||
|
|||
基于 Docker Compose,快速启动 MySQL、Oracle、PostgreSQL、SQL Server 等数据库。 |
|||
|
|||
注意!使用 Docker Compose 启动完测试数据后,因为会自动导入项目的 SQL 脚本,所以可能需要等待 1-2 分钟。 |
|||
|
|||
### 1.1 MySQL |
|||
|
|||
```Bash |
|||
docker compose up -d mysql |
|||
``` |
|||
|
|||
#### 1.2 Oracle |
|||
|
|||
```Bash |
|||
## x86 版本 |
|||
docker compose up -d oracle |
|||
|
|||
## MacBook Apple Silicon |
|||
docker compose up -d oracle_m1 |
|||
``` |
|||
|
|||
> 注意:如果使用 MacBook Apple Silicon 版本,它的 ORACLE_SID 不是 XE,而是 FREE!!! |
|||
|
|||
### 1.3 PostgreSQL |
|||
|
|||
```Bash |
|||
docker compose up -d postgres |
|||
``` |
|||
|
|||
### 1.4 SQL Server |
|||
|
|||
```Bash |
|||
docker compose up -d sqlserver |
|||
# 注意:启动完 sqlserver 后,需要手动再执行如下命令,因为 SQL Server 不支持初始化脚本 |
|||
docker compose exec sqlserver bash /tmp/create_schema.sh |
|||
``` |
|||
|
|||
### 1.5 DM 达梦 |
|||
|
|||
① 下载达梦 Docker 镜像:<https://eco.dameng.com/download/> 地址,点击“Docker 镜像”选项,进行下载。 |
|||
|
|||
② 加载镜像文件,在镜像 tar 文件所在目录运行: |
|||
|
|||
```Bash |
|||
docker load -i dm8_20240715_x86_rh6_rq_single.tar |
|||
``` |
|||
|
|||
③ 在项目 `sql/tools` 目录下运行: |
|||
|
|||
```Bash |
|||
docker compose up -d dm8 |
|||
# 注意:启动完 dm 后,需要手动再执行如下命令,因为 dm 不支持初始化脚本 |
|||
docker compose exec dm8 bash -c '/opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql' |
|||
exit |
|||
``` |
|||
|
|||
### 1.6 KingbaseES 人大金仓 |
|||
|
|||
① 下载人大金仓 Docker 镜像: |
|||
|
|||
* [x86_64 版本](https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/x86_64/kdb_x86_64_V009R001C001B0025.tar) 【Windows 选择这个】 |
|||
* [aarch64 版本](https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/aarch64/kdb_aarch64_V009R001C001B0025.tar) 【MacBook Apple Silicon 选择这个】 |
|||
|
|||
② 加载镜像文件,在镜像 tar 文件所在目录运行: |
|||
|
|||
```Bash |
|||
docker load -i kdb_x86_64_V009R001C001B0025.tar |
|||
``` |
|||
|
|||
③ 在项目 `sql/tools` 目录下运行: |
|||
|
|||
```Bash |
|||
docker compose up -d kingbase |
|||
# 注意:启动完 kingbase 后,需要手动再执行如下命令 |
|||
docker compose exec kingbase bash -c 'ksql -U $DB_USER -d test -f /tmp/schema.sql' |
|||
``` |
|||
|
|||
### 1.7 华为 OpenGauss |
|||
|
|||
```Bash |
|||
docker compose up -d opengauss |
|||
# 注意:启动完 opengauss 后,需要手动再执行如下命令 |
|||
docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql' |
|||
``` |
|||
|
|||
## 1.X 容器的销毁重建 |
|||
|
|||
开发测试过程中,有时候需要创建全新干净的数据库。由于测试数据 Docker 容器采用数据卷 Volume 挂载数据库实例的数据目录,因此销毁数据需要停止容器后,删除数据卷,然后再重新创建容器。 |
|||
|
|||
以 postgres 为例,操作如下: |
|||
|
|||
```Bash |
|||
docker compose down postgres |
|||
docker volume rm ruoyi-vue-pro_postgres |
|||
``` |
|||
|
|||
## 2. MySQL 转换其它数据库 |
|||
|
|||
项目提供了 `sql/tools/convertor.py` 脚本,支持将 MySQL 转换为 Oracle、PostgreSQL、SQL Server、达梦、人大金仓、OpenGauss 等数据库的脚本。 |
|||
|
|||
### 2.1 实现原理 |
|||
|
|||
通过读取 MySQL 的 `sql/mysql/ruoyi-vue-pro.sql` 数据库文件,转换成对应的数据库脚本。 |
|||
|
|||
### 2.2 使用方法 |
|||
|
|||
① 安装依赖库 `simple-ddl-parser` |
|||
|
|||
```bash |
|||
pip install simple-ddl-parser |
|||
# pip3 install simple-ddl-parser |
|||
``` |
|||
|
|||
② 在 `sql/tools/` 目录下,执行如下命令打印生成 postgres 的脚本内容,其他可选参数有:`oracle`、`sqlserver`、`dm8`、`kingbase`、`opengauss`: |
|||
|
|||
```Bash |
|||
python3 convertor.py postgres |
|||
# python3 convertor.py postgres > tmp.sql |
|||
``` |
|||
|
|||
程序将 SQL 脚本打印到终端,可以重定向到临时文件 `tmp.sql`。 |
|||
|
|||
确认无误后,可以利用 IDEA 进行格式化。当然,也可以直接导入到数据库中。 |
@ -0,0 +1,844 @@ |
|||
# encoding=utf8 |
|||
"""芋道系统数据库迁移工具 |
|||
|
|||
Author: dhb52 (https://gitee.com/dhb52) |
|||
|
|||
pip install simple-ddl-parser |
|||
""" |
|||
|
|||
import argparse |
|||
import pathlib |
|||
import re |
|||
import time |
|||
from abc import ABC, abstractmethod |
|||
from typing import Dict, Generator, Optional, Tuple, Union |
|||
|
|||
from simple_ddl_parser import DDLParser |
|||
|
|||
PREAMBLE = """/* |
|||
Win Database Transfer Tool |
|||
|
|||
Source Server Type : MySQL |
|||
|
|||
Target Server Type : {db_type} |
|||
|
|||
Date: {date} |
|||
*/ |
|||
|
|||
""" |
|||
|
|||
|
|||
def load_and_clean(sql_file: str) -> str: |
|||
"""加载源 SQL 文件,并清理内容方便下一步 ddl 解析 |
|||
|
|||
Args: |
|||
sql_file (str): sql文件路径 |
|||
|
|||
Returns: |
|||
str: 清理后的sql文件内容 |
|||
""" |
|||
REPLACE_PAIR_LIST = ( |
|||
(" CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ", " "), |
|||
(" KEY `", " INDEX `"), |
|||
("UNIQUE INDEX", "UNIQUE KEY"), |
|||
("b'0'", "'0'"), |
|||
("b'1'", "'1'"), |
|||
) |
|||
|
|||
content = open(sql_file).read() |
|||
for replace_pair in REPLACE_PAIR_LIST: |
|||
content = content.replace(*replace_pair) |
|||
content = re.sub(r"ENGINE.*COMMENT", "COMMENT", content) |
|||
content = re.sub(r"ENGINE.*;", ";", content) |
|||
return content |
|||
|
|||
|
|||
class Convertor(ABC): |
|||
def __init__(self, src: str, db_type) -> None: |
|||
self.src = src |
|||
self.db_type = db_type |
|||
self.content = load_and_clean(self.src) |
|||
self.table_script_list = re.findall(r"CREATE TABLE [^;]*;", self.content) |
|||
|
|||
@abstractmethod |
|||
def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]) -> str: |
|||
"""字段类型转换 |
|||
|
|||
Args: |
|||
type (str): 字段类型 |
|||
size (Optional[Union[int, Tuple[int]]]): 字段长度描述, 如varchar(255), decimal(10,2) |
|||
|
|||
Returns: |
|||
str: 类型定义 |
|||
""" |
|||
pass |
|||
|
|||
@abstractmethod |
|||
def gen_create(self, table_ddl: Dict) -> str: |
|||
"""生成 create 脚本 |
|||
|
|||
Args: |
|||
table_ddl (Dict): 表DDL |
|||
|
|||
Returns: |
|||
str: 生成脚本 |
|||
""" |
|||
pass |
|||
|
|||
@abstractmethod |
|||
def gen_pk(self, table_name: str) -> str: |
|||
"""生成主键定义 |
|||
|
|||
Args: |
|||
table_name (str): 表名 |
|||
|
|||
Returns: |
|||
str: 生成脚本 |
|||
""" |
|||
pass |
|||
|
|||
@abstractmethod |
|||
def gen_index(self, ddl: Dict) -> str: |
|||
"""生成索引定义 |
|||
|
|||
Args: |
|||
table_ddl (Dict): 表DDL |
|||
|
|||
Returns: |
|||
str: 生成脚本 |
|||
""" |
|||
pass |
|||
|
|||
@abstractmethod |
|||
def gen_comment(self, table_sql: str, table_name: str) -> str: |
|||
"""生成字段/表注释 |
|||
|
|||
Args: |
|||
table_sql (str): 原始表SQL |
|||
table_name (str): 表名 |
|||
|
|||
Returns: |
|||
str: 生成脚本 |
|||
""" |
|||
pass |
|||
|
|||
@abstractmethod |
|||
def gen_insert(self, table_name: str) -> str: |
|||
"""生成 insert 语句块 |
|||
|
|||
Args: |
|||
table_name (str): 表名 |
|||
|
|||
Returns: |
|||
str: 生成脚本 |
|||
""" |
|||
pass |
|||
|
|||
def gen_dual(self) -> str: |
|||
"""生成虚拟 dual 表 |
|||
|
|||
Returns: |
|||
str: 生成脚本, 默认返回空脚本, 表示当前数据库无需手工创建 |
|||
""" |
|||
return "" |
|||
|
|||
@staticmethod |
|||
def inserts(table_name: str, script_content: str) -> Generator: |
|||
PREFIX = f"INSERT INTO `{table_name}`" |
|||
|
|||
# 收集 `table_name` 对应的 insert 语句 |
|||
for line in script_content.split("\n"): |
|||
if line.startswith(PREFIX): |
|||
head, tail = line.replace(PREFIX, "").split(" VALUES ", maxsplit=1) |
|||
head = head.strip().replace("`", "").lower() |
|||
tail = tail.strip().replace(r"\"", '"') |
|||
# tail = tail.replace("b'0'", "'0'").replace("b'1'", "'1'") |
|||
yield f"INSERT INTO {table_name.lower()} {head} VALUES {tail}" |
|||
|
|||
@staticmethod |
|||
def index(ddl: Dict) -> Generator: |
|||
"""生成索引定义 |
|||
|
|||
Args: |
|||
ddl (Dict): 表DDL |
|||
|
|||
Yields: |
|||
Generator[str]: create index 语句 |
|||
""" |
|||
|
|||
def generate_columns(columns): |
|||
keys = [ |
|||
f"{col['name'].lower()}{' ' + col['order'].lower() if col['order'] != 'ASC' else ''}" |
|||
for col in columns[0] |
|||
] |
|||
return ", ".join(keys) |
|||
|
|||
for no, index in enumerate(ddl["index"], 1): |
|||
columns = generate_columns(index["columns"]) |
|||
table_name = ddl["table_name"].lower() |
|||
yield f"CREATE INDEX idx_{table_name}_{no:02d} ON {table_name} ({columns})" |
|||
|
|||
@staticmethod |
|||
def filed_comments(table_sql: str) -> Generator: |
|||
for line in table_sql.split("\n"): |
|||
match = re.match(r"^`([^`]+)`.* COMMENT '([^']+)'", line.strip()) |
|||
if match: |
|||
field = match.group(1) |
|||
comment_string = match.group(2).replace("\\n", "\n") |
|||
yield field, comment_string |
|||
|
|||
def table_comment(self, table_sql: str) -> str: |
|||
match = re.search(r"COMMENT \= '([^']+)';", table_sql) |
|||
return match.group(1) if match else None |
|||
|
|||
def print(self): |
|||
"""打印转换后的sql脚本到终端""" |
|||
print( |
|||
PREAMBLE.format( |
|||
db_type=self.db_type, |
|||
date=time.strftime("%Y-%m-%d %H:%M:%S"), |
|||
) |
|||
) |
|||
|
|||
dual = self.gen_dual() |
|||
if dual: |
|||
print( |
|||
f"""-- ---------------------------- |
|||
-- Table structure for dual |
|||
-- ---------------------------- |
|||
{dual} |
|||
""" |
|||
) |
|||
|
|||
error_scripts = [] |
|||
for table_sql in self.table_script_list: |
|||
ddl = DDLParser(table_sql.replace("`", "")).run() |
|||
|
|||
# 如果parse失败, 需要跟进 |
|||
if len(ddl) == 0: |
|||
error_scripts.append(table_sql) |
|||
continue |
|||
|
|||
table_ddl = ddl[0] |
|||
table_name = table_ddl["table_name"] |
|||
|
|||
# 忽略 quartz 的内容 |
|||
if table_name.lower().startswith("qrtz"): |
|||
continue |
|||
|
|||
# 为每个表生成个5个基本部分 |
|||
create = self.gen_create(table_ddl) |
|||
pk = self.gen_pk(table_name) |
|||
index = self.gen_index(table_ddl) |
|||
comment = self.gen_comment(table_sql, table_name) |
|||
inserts = self.gen_insert(table_name) |
|||
|
|||
# 组合当前表的DDL脚本 |
|||
script = f"""{create} |
|||
|
|||
{pk} |
|||
|
|||
{index} |
|||
|
|||
{comment} |
|||
|
|||
{inserts} |
|||
""" |
|||
|
|||
# 清理 |
|||
script = re.sub("\n{3,}", "\n\n", script).strip() + "\n" |
|||
|
|||
print(script) |
|||
|
|||
# 将parse失败的脚本打印出来 |
|||
if error_scripts: |
|||
for script in error_scripts: |
|||
print(script) |
|||
|
|||
|
|||
class PostgreSQLConvertor(Convertor): |
|||
def __init__(self, src): |
|||
super().__init__(src, "PostgreSQL") |
|||
|
|||
def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]): |
|||
"""类型转换""" |
|||
|
|||
type = type.lower() |
|||
|
|||
if type == "varchar": |
|||
return f"varchar({size})" |
|||
if type == "int": |
|||
return "int4" |
|||
if type == "bigint" or type == "bigint unsigned": |
|||
return "int8" |
|||
if type == "datetime": |
|||
return "timestamp" |
|||
if type == "bit": |
|||
return "bool" |
|||
if type in ("tinyint", "smallint"): |
|||
return "int2" |
|||
if type == "text": |
|||
return "text" |
|||
if type in ("blob", "mediumblob"): |
|||
return "bytea" |
|||
if type == "decimal": |
|||
return ( |
|||
f"numeric({','.join(str(s) for s in size)})" if len(size) else "numeric" |
|||
) |
|||
|
|||
def gen_create(self, ddl: Dict) -> str: |
|||
"""生成 create""" |
|||
|
|||
def _generate_column(col): |
|||
name = col["name"].lower() |
|||
if name == "deleted": |
|||
return "deleted int2 NOT NULL DEFAULT 0" |
|||
|
|||
type = col["type"].lower() |
|||
full_type = self.translate_type(type, col["size"]) |
|||
nullable = "NULL" if col["nullable"] else "NOT NULL" |
|||
default = f"DEFAULT {col['default']}" if col["default"] is not None else "" |
|||
return f"{name} {full_type} {nullable} {default}" |
|||
|
|||
table_name = ddl["table_name"].lower() |
|||
columns = [f"{_generate_column(col).strip()}" for col in ddl["columns"]] |
|||
filed_def_list = ",\n ".join(columns) |
|||
script = f"""-- ---------------------------- |
|||
-- Table structure for {table_name} |
|||
-- ---------------------------- |
|||
DROP TABLE IF EXISTS {table_name}; |
|||
CREATE TABLE {table_name} ( |
|||
{filed_def_list} |
|||
);""" |
|||
|
|||
return script |
|||
|
|||
def gen_index(self, ddl: Dict) -> str: |
|||
return "\n".join(f"{script};" for script in self.index(ddl)) |
|||
|
|||
def gen_comment(self, table_sql: str, table_name: str) -> str: |
|||
"""生成字段及表的注释""" |
|||
|
|||
script = "" |
|||
for field, comment_string in self.filed_comments(table_sql): |
|||
script += ( |
|||
f"COMMENT ON COLUMN {table_name}.{field} IS '{comment_string}';" + "\n" |
|||
) |
|||
|
|||
table_comment = self.table_comment(table_sql) |
|||
if table_comment: |
|||
script += f"COMMENT ON TABLE {table_name} IS '{table_comment}';\n" |
|||
|
|||
return script |
|||
|
|||
def gen_pk(self, table_name) -> str: |
|||
"""生成主键定义""" |
|||
return f"ALTER TABLE {table_name} ADD CONSTRAINT pk_{table_name} PRIMARY KEY (id);\n" |
|||
|
|||
def gen_insert(self, table_name: str) -> str: |
|||
"""生成 insert 语句,以及根据最后的 insert id+1 生成 Sequence""" |
|||
|
|||
inserts = list(Convertor.inserts(table_name, self.content)) |
|||
## 生成 insert 脚本 |
|||
script = "" |
|||
last_id = 0 |
|||
if inserts: |
|||
inserts_lines = "\n".join(inserts) |
|||
script += f"""\n\n-- ---------------------------- |
|||
-- Records of {table_name.lower()} |
|||
-- ---------------------------- |
|||
-- @formatter:off |
|||
BEGIN; |
|||
{inserts_lines} |
|||
COMMIT; |
|||
-- @formatter:on""" |
|||
match = re.search(r"VALUES \((\d+),", inserts[-1]) |
|||
if match: |
|||
last_id = int(match.group(1)) |
|||
|
|||
# 生成 Sequence |
|||
script += ( |
|||
"\n\n" |
|||
+ f"""DROP SEQUENCE IF EXISTS {table_name}_seq; |
|||
CREATE SEQUENCE {table_name}_seq |
|||
START {last_id + 1};""" |
|||
) |
|||
|
|||
return script |
|||
|
|||
def gen_dual(self) -> str: |
|||
return """DROP TABLE IF EXISTS dual; |
|||
CREATE TABLE dual |
|||
( |
|||
id int2 |
|||
); |
|||
|
|||
COMMENT ON TABLE dual IS '数据库连接的表'; |
|||
|
|||
-- ---------------------------- |
|||
-- Records of dual |
|||
-- ---------------------------- |
|||
-- @formatter:off |
|||
INSERT INTO dual VALUES (1); |
|||
-- @formatter:on""" |
|||
|
|||
|
|||
class OracleConvertor(Convertor): |
|||
def __init__(self, src): |
|||
super().__init__(src, "Oracle") |
|||
|
|||
def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]): |
|||
"""类型转换""" |
|||
type = type.lower() |
|||
|
|||
if type == "varchar": |
|||
return f"varchar2({size if size < 4000 else 4000})" |
|||
if type == "int": |
|||
return "number" |
|||
if type == "bigint" or type == "bigint unsigned": |
|||
return "number" |
|||
if type == "datetime": |
|||
return "date" |
|||
if type == "bit": |
|||
return "number(1,0)" |
|||
if type in ("tinyint", "smallint"): |
|||
return "smallint" |
|||
if type == "text": |
|||
return "clob" |
|||
if type in ("blob", "mediumblob"): |
|||
return "blob" |
|||
if type == "decimal": |
|||
return ( |
|||
f"number({','.join(str(s) for s in size)})" if len(size) else "number" |
|||
) |
|||
|
|||
def gen_create(self, ddl) -> str: |
|||
"""生成 CREATE 语句""" |
|||
|
|||
def generate_column(col): |
|||
name = col["name"].lower() |
|||
if name == "deleted": |
|||
return "deleted number(1,0) DEFAULT 0 NOT NULL" |
|||
|
|||
type = col["type"].lower() |
|||
full_type = self.translate_type(type, col["size"]) |
|||
nullable = "NULL" if col["nullable"] else "NOT NULL" |
|||
default = f"DEFAULT {col['default']}" if col["default"] is not None else "" |
|||
# Oracle 中 size 不能作为字段名 |
|||
field_name = '"size"' if name == "size" else name |
|||
# Oracle DEFAULT 定义在 NULLABLE 之前 |
|||
return f"{field_name} {full_type} {default} {nullable}" |
|||
|
|||
table_name = ddl["table_name"].lower() |
|||
columns = [f"{generate_column(col).strip()}" for col in ddl["columns"]] |
|||
field_def_list = ",\n ".join(columns) |
|||
script = f"""-- ---------------------------- |
|||
-- Table structure for {table_name} |
|||
-- ---------------------------- |
|||
CREATE TABLE {table_name} ( |
|||
{field_def_list} |
|||
);""" |
|||
|
|||
# oracle INSERT '' 不能通过 NOT NULL 校验 |
|||
script = script.replace("DEFAULT '' NOT NULL", "DEFAULT '' NULL") |
|||
|
|||
return script |
|||
|
|||
def gen_index(self, ddl: Dict) -> str: |
|||
return "\n".join(f"{script};" for script in self.index(ddl)) |
|||
|
|||
def gen_comment(self, table_sql: str, table_name: str) -> str: |
|||
script = "" |
|||
for field, comment_string in self.filed_comments(table_sql): |
|||
script += ( |
|||
f"COMMENT ON COLUMN {table_name}.{field} IS '{comment_string}';" + "\n" |
|||
) |
|||
|
|||
table_comment = self.table_comment(table_sql) |
|||
if table_comment: |
|||
script += f"COMMENT ON TABLE {table_name} IS '{table_comment}';\n" |
|||
|
|||
return script |
|||
|
|||
def gen_pk(self, table_name: str) -> str: |
|||
"""生成主键定义""" |
|||
return f"ALTER TABLE {table_name} ADD CONSTRAINT pk_{table_name} PRIMARY KEY (id);\n" |
|||
|
|||
def gen_index(self, ddl: Dict) -> str: |
|||
return "\n".join(f"{script};" for script in self.index(ddl)) |
|||
|
|||
def gen_insert(self, table_name: str) -> str: |
|||
"""拷贝 INSERT 语句""" |
|||
inserts = [] |
|||
for insert_script in Convertor.inserts(table_name, self.content): |
|||
# 对日期数据添加 TO_DATE 转换 |
|||
insert_script = re.sub( |
|||
r"('\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}')", |
|||
r"to_date(\g<1>, 'SYYYY-MM-DD HH24:MI:SS')", |
|||
insert_script, |
|||
) |
|||
inserts.append(insert_script) |
|||
|
|||
## 生成 insert 脚本 |
|||
script = "" |
|||
last_id = 0 |
|||
if inserts: |
|||
inserts_lines = "\n".join(inserts) |
|||
script += f"""\n\n-- ---------------------------- |
|||
-- Records of {table_name.lower()} |
|||
-- ---------------------------- |
|||
-- @formatter:off |
|||
{inserts_lines} |
|||
COMMIT; |
|||
-- @formatter:on""" |
|||
match = re.search(r"VALUES \((\d+),", inserts[-1]) |
|||
if match: |
|||
last_id = int(match.group(1)) |
|||
|
|||
# 生成 Sequence |
|||
script += f""" |
|||
|
|||
CREATE SEQUENCE {table_name}_seq |
|||
START WITH {last_id + 1};""" |
|||
|
|||
return script |
|||
|
|||
|
|||
class SQLServerConvertor(Convertor): |
|||
"""_summary_ |
|||
|
|||
Args: |
|||
Convertor (_type_): _description_ |
|||
""" |
|||
|
|||
def __init__(self, src): |
|||
super().__init__(src, "Microsoft SQL Server") |
|||
|
|||
def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]): |
|||
"""类型转换""" |
|||
|
|||
type = type.lower() |
|||
|
|||
if type == "varchar": |
|||
return f"nvarchar({size if size < 4000 else 4000})" |
|||
if type == "int": |
|||
return "int" |
|||
if type == "bigint" or type == "bigint unsigned": |
|||
return "bigint" |
|||
if type == "datetime": |
|||
return "datetime2" |
|||
if type == "bit": |
|||
return "varchar(1)" |
|||
if type in ("tinyint", "smallint"): |
|||
return "tinyint" |
|||
if type == "text": |
|||
return "nvarchar(max)" |
|||
if type in ("blob", "mediumblob"): |
|||
return "varbinary(max)" |
|||
if type == "decimal": |
|||
return ( |
|||
f"numeric({','.join(str(s) for s in size)})" if len(size) else "numeric" |
|||
) |
|||
|
|||
def gen_create(self, ddl: Dict) -> str: |
|||
"""生成 create""" |
|||
|
|||
def _generate_column(col): |
|||
name = col["name"].lower() |
|||
if name == "id": |
|||
return "id bigint NOT NULL PRIMARY KEY IDENTITY" |
|||
if name == "deleted": |
|||
return "deleted bit DEFAULT 0 NOT NULL" |
|||
|
|||
type = col["type"].lower() |
|||
full_type = self.translate_type(type, col["size"]) |
|||
nullable = "NULL" if col["nullable"] else "NOT NULL" |
|||
default = f"DEFAULT {col['default']}" if col["default"] is not None else "" |
|||
return f"{name} {full_type} {default} {nullable}" |
|||
|
|||
table_name = ddl["table_name"].lower() |
|||
columns = [f"{_generate_column(col).strip()}" for col in ddl["columns"]] |
|||
filed_def_list = ",\n ".join(columns) |
|||
script = f"""-- ---------------------------- |
|||
-- Table structure for {table_name} |
|||
-- ---------------------------- |
|||
DROP TABLE IF EXISTS {table_name} |
|||
GO |
|||
CREATE TABLE {table_name} ( |
|||
{filed_def_list} |
|||
) |
|||
GO""" |
|||
|
|||
return script |
|||
|
|||
def gen_comment(self, table_sql: str, table_name: str) -> str: |
|||
"""生成字段及表的注释""" |
|||
|
|||
script = "" |
|||
|
|||
for field, comment_string in self.filed_comments(table_sql): |
|||
script += f"""EXEC sp_addextendedproperty |
|||
'MS_Description', N'{comment_string}', |
|||
'SCHEMA', N'dbo', |
|||
'TABLE', N'{table_name}', |
|||
'COLUMN', N'{field}' |
|||
GO |
|||
|
|||
""" |
|||
|
|||
table_comment = self.table_comment(table_sql) |
|||
if table_comment: |
|||
script += f"""EXEC sp_addextendedproperty |
|||
'MS_Description', N'{table_comment}', |
|||
'SCHEMA', N'dbo', |
|||
'TABLE', N'{table_name}' |
|||
GO |
|||
|
|||
""" |
|||
return script |
|||
|
|||
def gen_pk(self, table_name: str) -> str: |
|||
"""生成主键定义""" |
|||
return "" |
|||
|
|||
def gen_index(self, ddl: Dict) -> str: |
|||
"""生成 index""" |
|||
return "\n".join(f"{script}\nGO" for script in self.index(ddl)) |
|||
|
|||
def gen_insert(self, table_name: str) -> str: |
|||
"""生成 insert 语句""" |
|||
|
|||
# 收集 `table_name` 对应的 insert 语句 |
|||
inserts = [] |
|||
for insert_script in Convertor.inserts(table_name, self.content): |
|||
# SQLServer: 字符串前加N,hack,是否存在替换字符串内容的风险 |
|||
insert_script = insert_script.replace(", '", ", N'").replace( |
|||
"VALUES ('", "VALUES (N')" |
|||
) |
|||
# 删除 insert 的结尾分号 |
|||
insert_script = re.sub(";$", r"\nGO", insert_script) |
|||
inserts.append(insert_script) |
|||
|
|||
## 生成 insert 脚本 |
|||
script = "" |
|||
if inserts: |
|||
inserts_lines = "\n".join(inserts) |
|||
script += f"""\n\n-- ---------------------------- |
|||
-- Records of {table_name.lower()} |
|||
-- ---------------------------- |
|||
-- @formatter:off |
|||
BEGIN TRANSACTION |
|||
GO |
|||
SET IDENTITY_INSERT {table_name.lower()} ON |
|||
GO |
|||
{inserts_lines} |
|||
SET IDENTITY_INSERT {table_name.lower()} OFF |
|||
GO |
|||
COMMIT |
|||
GO |
|||
-- @formatter:on""" |
|||
|
|||
return script |
|||
|
|||
def gen_dual(self) -> str: |
|||
return """DROP TABLE IF EXISTS dual |
|||
GO |
|||
CREATE TABLE dual |
|||
( |
|||
id int |
|||
) |
|||
GO |
|||
|
|||
EXEC sp_addextendedproperty |
|||
'MS_Description', N'数据库连接的表', |
|||
'SCHEMA', N'dbo', |
|||
'TABLE', N'dual' |
|||
GO |
|||
|
|||
-- ---------------------------- |
|||
-- Records of dual |
|||
-- ---------------------------- |
|||
-- @formatter:off |
|||
INSERT INTO dual VALUES (1) |
|||
GO |
|||
-- @formatter:on""" |
|||
|
|||
|
|||
class DM8Convertor(Convertor): |
|||
def __init__(self, src): |
|||
super().__init__(src, "DM8") |
|||
|
|||
def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]): |
|||
"""类型转换""" |
|||
type = type.lower() |
|||
|
|||
if type == "varchar": |
|||
return f"varchar({size})" |
|||
if type == "int": |
|||
return "int" |
|||
if type == "bigint" or type == "bigint unsigned": |
|||
return "bigint" |
|||
if type == "datetime": |
|||
return "datetime" |
|||
if type == "bit": |
|||
return "bit" |
|||
if type in ("tinyint", "smallint"): |
|||
return "smallint" |
|||
if type == "text": |
|||
return "text" |
|||
if type == "blob": |
|||
return "blob" |
|||
if type == "mediumblob": |
|||
return "varchar(10240)" |
|||
if type == "decimal": |
|||
return ( |
|||
f"decimal({','.join(str(s) for s in size)})" if len(size) else "decimal" |
|||
) |
|||
|
|||
def gen_create(self, ddl) -> str: |
|||
"""生成 CREATE 语句""" |
|||
|
|||
def generate_column(col): |
|||
name = col["name"].lower() |
|||
if name == "id": |
|||
return "id bigint NOT NULL PRIMARY KEY IDENTITY" |
|||
|
|||
type = col["type"].lower() |
|||
full_type = self.translate_type(type, col["size"]) |
|||
nullable = "NULL" if col["nullable"] else "NOT NULL" |
|||
default = f"DEFAULT {col['default']}" if col["default"] is not None else "" |
|||
return f"{name} {full_type} {default} {nullable}" |
|||
|
|||
table_name = ddl["table_name"].lower() |
|||
columns = [f"{generate_column(col).strip()}" for col in ddl["columns"]] |
|||
field_def_list = ",\n ".join(columns) |
|||
script = f"""-- ---------------------------- |
|||
-- Table structure for {table_name} |
|||
-- ---------------------------- |
|||
CREATE TABLE {table_name} ( |
|||
{field_def_list} |
|||
);""" |
|||
|
|||
# oracle INSERT '' 不能通过 NOT NULL 校验 |
|||
script = script.replace("DEFAULT '' NOT NULL", "DEFAULT '' NULL") |
|||
|
|||
return script |
|||
|
|||
def gen_index(self, ddl: Dict) -> str: |
|||
return "\n".join(f"{script};" for script in self.index(ddl)) |
|||
|
|||
def gen_comment(self, table_sql: str, table_name: str) -> str: |
|||
script = "" |
|||
for field, comment_string in self.filed_comments(table_sql): |
|||
script += ( |
|||
f"COMMENT ON COLUMN {table_name}.{field} IS '{comment_string}';" + "\n" |
|||
) |
|||
|
|||
table_comment = self.table_comment(table_sql) |
|||
if table_comment: |
|||
script += f"COMMENT ON TABLE {table_name} IS '{table_comment}';\n" |
|||
|
|||
return script |
|||
|
|||
def gen_pk(self, table_name: str) -> str: |
|||
"""生成主键定义""" |
|||
return "" |
|||
|
|||
def gen_index(self, ddl: Dict) -> str: |
|||
return "\n".join(f"{script};" for script in self.index(ddl)) |
|||
|
|||
def gen_insert(self, table_name: str) -> str: |
|||
"""拷贝 INSERT 语句""" |
|||
inserts = list(Convertor.inserts(table_name, self.content)) |
|||
|
|||
## 生成 insert 脚本 |
|||
script = "" |
|||
if inserts: |
|||
inserts_lines = "\n".join(inserts) |
|||
script += f"""\n\n-- ---------------------------- |
|||
-- Records of {table_name.lower()} |
|||
-- ---------------------------- |
|||
-- @formatter:off |
|||
SET IDENTITY_INSERT {table_name.lower()} ON; |
|||
{inserts_lines} |
|||
COMMIT; |
|||
SET IDENTITY_INSERT {table_name.lower()} OFF; |
|||
-- @formatter:on""" |
|||
|
|||
return script |
|||
|
|||
|
|||
class KingbaseConvertor(PostgreSQLConvertor): |
|||
def __init__(self, src): |
|||
super().__init__(src) |
|||
self.db_type = "Kingbase" |
|||
|
|||
def gen_create(self, ddl: Dict) -> str: |
|||
"""生成 create""" |
|||
|
|||
def _generate_column(col): |
|||
name = col["name"].lower() |
|||
if name == "deleted": |
|||
return "deleted int2 NOT NULL DEFAULT 0" |
|||
|
|||
type = col["type"].lower() |
|||
full_type = self.translate_type(type, col["size"]) |
|||
nullable = "NULL" if col["nullable"] else "NOT NULL" |
|||
default = f"DEFAULT {col['default']}" if col["default"] is not None else "" |
|||
return f"{name} {full_type} {nullable} {default}" |
|||
|
|||
table_name = ddl["table_name"].lower() |
|||
columns = [f"{_generate_column(col).strip()}" for col in ddl["columns"]] |
|||
filed_def_list = ",\n ".join(columns) |
|||
script = f"""-- ---------------------------- |
|||
-- Table structure for {table_name} |
|||
-- ---------------------------- |
|||
DROP TABLE IF EXISTS {table_name}; |
|||
CREATE TABLE {table_name} ( |
|||
{filed_def_list} |
|||
);""" |
|||
|
|||
# Kingbase INSERT '' 不能通过 NOT NULL 校验 |
|||
script = script.replace("NOT NULL DEFAULT ''", "NULL DEFAULT ''") |
|||
|
|||
return script |
|||
|
|||
|
|||
class OpengaussConvertor(KingbaseConvertor): |
|||
def __init__(self, src): |
|||
super().__init__(src) |
|||
self.db_type = "OpenGauss" |
|||
|
|||
|
|||
def main(): |
|||
parser = argparse.ArgumentParser(description="芋道系统数据库转换工具") |
|||
parser.add_argument( |
|||
"type", |
|||
type=str, |
|||
help="目标数据库类型", |
|||
choices=["postgres", "oracle", "sqlserver", "dm8", "kingbase", "opengauss"], |
|||
) |
|||
args = parser.parse_args() |
|||
|
|||
sql_file = pathlib.Path("../mysql/ruoyi-vue-pro.sql").resolve().as_posix() |
|||
convertor = None |
|||
if args.type == "postgres": |
|||
convertor = PostgreSQLConvertor(sql_file) |
|||
elif args.type == "oracle": |
|||
convertor = OracleConvertor(sql_file) |
|||
elif args.type == "sqlserver": |
|||
convertor = SQLServerConvertor(sql_file) |
|||
elif args.type == "dm8": |
|||
convertor = DM8Convertor(sql_file) |
|||
elif args.type == "kingbase": |
|||
convertor = KingbaseConvertor(sql_file) |
|||
elif args.type == "opengauss": |
|||
convertor = OpengaussConvertor(sql_file) |
|||
else: |
|||
raise NotImplementedError(f"不支持目标数据库类型: {args.type}") |
|||
|
|||
convertor.print() |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
main() |
@ -0,0 +1,134 @@ |
|||
name: ruoyi-vue-pro |
|||
|
|||
volumes: |
|||
mysql: { } |
|||
postgres: { } |
|||
sqlserver: { } |
|||
dm8: { } |
|||
kingbase: { } |
|||
opengauss: { } |
|||
|
|||
services: |
|||
mysql: |
|||
image: mysql:8.0.33 |
|||
restart: unless-stopped |
|||
environment: |
|||
TZ: Asia/Shanghai |
|||
MYSQL_ROOT_PASSWORD: 123456 |
|||
MYSQL_DATABASE: ruoyi-vue-pro |
|||
ports: |
|||
- "3306:3306" |
|||
volumes: |
|||
- mysql:/var/lib/mysql/ |
|||
# 注入初始化脚本 |
|||
- ./mysql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/init.sql:ro |
|||
command: |
|||
--default-authentication-plugin=mysql_native_password |
|||
--character-set-server=utf8mb4 |
|||
--collation-server=utf8mb4_general_ci |
|||
--explicit_defaults_for_timestamp=true |
|||
--lower_case_table_names=1 |
|||
|
|||
postgres: |
|||
image: postgres:14.2 |
|||
restart: unless-stopped |
|||
environment: |
|||
POSTGRES_USER: root |
|||
POSTGRES_PASSWORD: 123456 |
|||
POSTGRES_DB: ruoyi-vue-pro |
|||
ports: |
|||
- "5432:5432" |
|||
volumes: |
|||
- postgres:/var/lib/postgresql/data |
|||
# 注入初始化脚本 |
|||
- ../postgresql/quartz.sql:/docker-entrypoint-initdb.d/quartz.sql:ro |
|||
- ../postgresql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/ruoyi-vue-pro.sql:ro |
|||
|
|||
oracle: |
|||
image: gvenzl/oracle-xe:18-slim-faststart |
|||
restart: unless-stopped |
|||
environment: |
|||
## 登录信息 SID: XE user: system password: oracle |
|||
ORACLE_PASSWORD: oracle |
|||
ports: |
|||
- "1521:1521" |
|||
volumes: |
|||
- ../oracle/ruoyi-vue-pro.sql:/tmp/schema.sql:ro |
|||
# 创建app用户: ROOT/123456@//localhost/XEPDB1 |
|||
- ./oracle/1_create_user.sql:/docker-entrypoint-initdb.d/1_create_user.sql:ro |
|||
- ./oracle/2_create_schema.sh:/docker-entrypoint-initdb.d/2_create_schema.sh:ro |
|||
|
|||
oracle_m1: |
|||
image: einslib/oracle-19c:19.3.0-ee-slim-faststart |
|||
restart: unless-stopped |
|||
environment: |
|||
## 登录信息 SID: FREE user: system password: oracle |
|||
ORACLE_PASSWORD: oracle |
|||
ports: |
|||
- "1521:1521" |
|||
volumes: |
|||
- ../oracle/ruoyi-vue-pro.sql:/tmp/schema.sql:ro |
|||
# 创建app用户: ROOT/123456@//localhost/XEPDB1 |
|||
- ./oracle/1_create_user.sql:/docker-entrypoint-initdb.d/1_create_user.sql:ro |
|||
- ./oracle/2_create_schema.sh:/docker-entrypoint-initdb.d/2_create_schema.sh:ro |
|||
|
|||
sqlserver: |
|||
image: mcr.microsoft.com/mssql/server:2017-latest |
|||
restart: unless-stopped |
|||
environment: |
|||
TZ: Asia/Shanghai |
|||
ACCEPT_EULA: "Y" |
|||
SA_PASSWORD: "Win@2024" |
|||
ports: |
|||
- "1433:1433" |
|||
volumes: |
|||
- sqlserver:/var/opt/mssql |
|||
- ../sqlserver/ruoyi-vue-pro.sql:/tmp/schema.sql:ro |
|||
# docker compose exec sqlserver bash /tmp/create_schema.sh |
|||
- ./sqlserver/create_schema.sh:/tmp/create_schema.sh:ro |
|||
|
|||
dm8: |
|||
# docker load -i dm8_20240715_x86_rh6_rq_single.tar |
|||
image: dm8_single:dm8_20240715_rev232765_x86_rh6_64 |
|||
restart: unless-stopped |
|||
environment: |
|||
PAGE_SIZE: 16 |
|||
LD_LIBRARY_PATH: /opt/dmdbms/bin |
|||
EXTENT_SIZE: 32 |
|||
BLANK_PAD_MODE: 1 |
|||
LOG_SIZE: 1024 |
|||
UNICODE_FLAG: 1 |
|||
LENGTH_IN_CHAR: 1 |
|||
INSTANCE_NAME: dm8_test |
|||
ports: |
|||
- "5236:5236" |
|||
volumes: |
|||
- dm8:/opt/dmdbms/data |
|||
- ../dm/ruoyi-vue-pro-dm8.sql:/tmp/schema.sql:ro |
|||
|
|||
kingbase: |
|||
image: kingbase_v009r001c001b0025_single_x86:v1 |
|||
# image: kingbase_v009r001c001b0025_single_arm:v1 |
|||
restart: unless-stopped |
|||
environment: |
|||
DB_USER: root |
|||
DB_PASSWORD: 123456 |
|||
ports: |
|||
- "54321:54321" |
|||
volumes: |
|||
- kingbase:/home/kingbase/userdata |
|||
- ../kingbase/ruoyi-vue-pro.sql:/tmp/schema.sql:ro |
|||
|
|||
opengauss: |
|||
image: opengauss/opengauss:5.0.0 |
|||
restart: unless-stopped |
|||
environment: |
|||
GS_USERNAME: root |
|||
GS_PASSWORD: Win@2024 |
|||
LD_LIBRARY_PATH: /usr/local/opengauss/lib:/usr/lib |
|||
ports: |
|||
- "5432:5432" |
|||
volumes: |
|||
- opengauss:/var/lib/opengauss |
|||
- ../opengauss/ruoyi-vue-pro.sql:/tmp/schema.sql:ro |
|||
# docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql' |
@ -0,0 +1,3 @@ |
|||
ALTER SESSION SET CONTAINER=XEPDB1; |
|||
CREATE USER ROOT IDENTIFIED BY 123456 QUOTA UNLIMITED ON USERS; |
|||
GRANT CONNECT, RESOURCE TO ROOT; |
@ -0,0 +1 @@ |
|||
sqlplus -s ROOT/123456@//localhost/XEPDB1 @/tmp/schema.sql |
@ -0,0 +1,5 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${SA_PASSWORD} -Q "CREATE DATABASE [ruoyi-vue-pro]; |
|||
GO" |
|||
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${SA_PASSWORD} -d 'ruoyi-vue-pro' -i /tmp/schema.sql |
@ -0,0 +1,633 @@ |
|||
<?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> |
|||
|
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-dependencies</artifactId> |
|||
<version>${revision}</version> |
|||
<packaging>pom</packaging> |
|||
|
|||
<name>${project.artifactId}</name> |
|||
<description>基础 bom 文件,管理整个项目的依赖版本</description> |
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
|||
|
|||
<properties> |
|||
<revision>2.3.0-SNAPSHOT</revision> |
|||
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> |
|||
<!-- 统一依赖管理 --> |
|||
<spring.boot.version>3.3.4</spring.boot.version> |
|||
<!-- Web 相关 --> |
|||
<springdoc.version>2.3.0</springdoc.version> |
|||
<knife4j.version>4.5.0</knife4j.version> |
|||
<!-- DB 相关 --> |
|||
<druid.version>1.2.23</druid.version> |
|||
<mybatis.version>3.5.16</mybatis.version> |
|||
<mybatis-plus.version>3.5.8</mybatis-plus.version> |
|||
<dynamic-datasource.version>4.3.1</dynamic-datasource.version> |
|||
<mybatis-plus-join.version>1.4.13</mybatis-plus-join.version> |
|||
<easy-trans.version>3.0.6</easy-trans.version> |
|||
<redisson.version>3.36.0</redisson.version> |
|||
<dm8.jdbc.version>8.1.3.140</dm8.jdbc.version> |
|||
<kingbase.jdbc.version>8.6.0</kingbase.jdbc.version> |
|||
<opengauss.jdbc.version>5.1.0</opengauss.jdbc.version> |
|||
<!-- 消息队列 --> |
|||
<rocketmq-spring.version>2.3.1</rocketmq-spring.version> |
|||
<!-- 服务保障相关 --> |
|||
<lock4j.version>2.2.7</lock4j.version> |
|||
<!-- 监控相关 --> |
|||
<skywalking.version>9.0.0</skywalking.version> |
|||
<spring-boot-admin.version>3.3.3</spring-boot-admin.version> |
|||
<opentracing.version>0.33.0</opentracing.version> |
|||
<!-- Test 测试相关 --> |
|||
<podam.version>8.0.2.RELEASE</podam.version> |
|||
<jedis-mock.version>1.1.4</jedis-mock.version> |
|||
<mockito-inline.version>5.2.0</mockito-inline.version> |
|||
<!-- Bpm 工作流相关 --> |
|||
<flowable.version>7.0.1</flowable.version> |
|||
<!-- 工具类相关 --> |
|||
<captcha-plus.version>2.0.3</captcha-plus.version> |
|||
<jsoup.version>1.18.1</jsoup.version> |
|||
<lombok.version>1.18.34</lombok.version> |
|||
<mapstruct.version>1.6.2</mapstruct.version> |
|||
<hutool-5.version>5.8.32</hutool-5.version> |
|||
<hutool-6.version>6.0.0-M16</hutool-6.version> |
|||
<easyexcel.verion>4.0.3</easyexcel.verion> |
|||
<velocity.version>2.4</velocity.version> |
|||
<fastjson.version>1.2.83</fastjson.version> |
|||
<guava.version>33.3.1-jre</guava.version> |
|||
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version> |
|||
<commons-net.version>3.11.1</commons-net.version> |
|||
<jsch.version>0.1.55</jsch.version> |
|||
<tika-core.version>2.9.2</tika-core.version> |
|||
<ip2region.version>2.7.0</ip2region.version> |
|||
<bizlog-sdk.version>3.0.6</bizlog-sdk.version> |
|||
<netty.version>4.1.113.Final</netty.version> |
|||
<mqtt.version>1.2.5</mqtt.version> |
|||
<!-- 三方云服务相关 --> |
|||
<okio.version>3.5.0</okio.version> |
|||
<okhttp3.version>4.11.0</okhttp3.version> |
|||
<commons-io.version>2.17.0</commons-io.version> |
|||
<commons-compress.version>1.27.1</commons-compress.version> |
|||
<minio.version>8.5.7</minio.version> |
|||
<justauth.version>2.0.5</justauth.version> |
|||
<jimureport.version>1.8.1</jimureport.version> |
|||
<weixin-java.version>4.6.5.B</weixin-java.version> |
|||
</properties> |
|||
|
|||
<dependencyManagement> |
|||
<dependencies> |
|||
<!-- 统一依赖管理 --> |
|||
<dependency> |
|||
<groupId>io.netty</groupId> |
|||
<artifactId>netty-bom</artifactId> |
|||
<version>${netty.version}</version> |
|||
<type>pom</type> |
|||
<scope>import</scope> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-dependencies</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
<type>pom</type> |
|||
<scope>import</scope> |
|||
</dependency> |
|||
|
|||
<!-- 业务组件 --> |
|||
<dependency> |
|||
<groupId>io.github.mouzt</groupId> |
|||
<artifactId>bizlog-sdk</artifactId> |
|||
<version>${bizlog-sdk.version}</version> |
|||
<exclusions> |
|||
<exclusion> <!-- 排除掉springboot依赖使用项目的 --> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-biz-tenant</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-biz-data-permission</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-biz-ip</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<!-- Spring 核心 --> |
|||
<dependency> |
|||
<!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 --> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-configuration-processor</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
</dependency> |
|||
|
|||
<!-- Web 相关 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-web</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-security</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-websocket</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.github.xiaoymin</groupId> |
|||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> |
|||
<version>${knife4j.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springdoc</groupId> |
|||
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId> |
|||
<version>${springdoc.version}</version> |
|||
</dependency> |
|||
|
|||
<!-- DB 相关 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-mybatis</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>druid-spring-boot-3-starter</artifactId> |
|||
<version>${druid.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.mybatis</groupId> |
|||
<artifactId>mybatis</artifactId> |
|||
<version>${mybatis.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId> |
|||
<version>${mybatis-plus.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 --> |
|||
<version>${mybatis-plus.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId> <!-- 多数据源 --> |
|||
<version>${dynamic-datasource.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.github.yulichang</groupId> |
|||
<artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 --> |
|||
<version>${mybatis-plus-join.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 --> |
|||
<artifactId>easy-trans-spring-boot-starter</artifactId> |
|||
<version>${easy-trans.version}</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-context</artifactId> |
|||
</exclusion> |
|||
<exclusion> |
|||
<groupId>org.springframework.cloud</groupId> |
|||
<artifactId>spring-cloud-commons</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.fhs-opensource</groupId> |
|||
<artifactId>easy-trans-mybatis-plus-extend</artifactId> |
|||
<version>${easy-trans.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.fhs-opensource</groupId> |
|||
<artifactId>easy-trans-anno</artifactId> |
|||
<version>${easy-trans.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-redis</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.redisson</groupId> |
|||
<artifactId>redisson-spring-boot-starter</artifactId> |
|||
<version>${redisson.version}</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-actuator</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.dameng</groupId> |
|||
<artifactId>DmJdbcDriver18</artifactId> |
|||
<version>${dm8.jdbc.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.opengauss</groupId> |
|||
<artifactId>opengauss-jdbc</artifactId> |
|||
<version>${opengauss.jdbc.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>cn.com.kingbase</groupId> |
|||
<artifactId>kingbase8</artifactId> |
|||
<version>${kingbase.jdbc.version}</version> |
|||
</dependency> |
|||
|
|||
<!-- Job 定时任务相关 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-job</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<!-- 消息队列相关 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-mq</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-starter</artifactId> |
|||
<version>${rocketmq-spring.version}</version> |
|||
</dependency> |
|||
|
|||
<!-- 服务保障相关 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-protection</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>lock4j-redisson-spring-boot-starter</artifactId> |
|||
<version>${lock4j.version}</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<artifactId>redisson-spring-boot-starter</artifactId> |
|||
<groupId>org.redisson</groupId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<!-- 监控相关 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-monitor</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.apache.skywalking</groupId> |
|||
<artifactId>apm-toolkit-trace</artifactId> |
|||
<version>${skywalking.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.apache.skywalking</groupId> |
|||
<artifactId>apm-toolkit-logback-1.x</artifactId> |
|||
<version>${skywalking.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.apache.skywalking</groupId> |
|||
<artifactId>apm-toolkit-opentracing</artifactId> |
|||
<version>${skywalking.version}</version> |
|||
<!-- <exclusions>--> |
|||
<!-- <exclusion>--> |
|||
<!-- <artifactId>opentracing-api</artifactId>--> |
|||
<!-- <groupId>io.opentracing</groupId>--> |
|||
<!-- </exclusion>--> |
|||
<!-- <exclusion>--> |
|||
<!-- <artifactId>opentracing-util</artifactId>--> |
|||
<!-- <groupId>io.opentracing</groupId>--> |
|||
<!-- </exclusion>--> |
|||
<!-- </exclusions>--> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>io.opentracing</groupId> |
|||
<artifactId>opentracing-api</artifactId> |
|||
<version>${opentracing.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>io.opentracing</groupId> |
|||
<artifactId>opentracing-util</artifactId> |
|||
<version>${opentracing.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>io.opentracing</groupId> |
|||
<artifactId>opentracing-noop</artifactId> |
|||
<version>${opentracing.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>de.codecentric</groupId> |
|||
<artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 --> |
|||
<version>${spring-boot-admin.version}</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>de.codecentric</groupId> |
|||
<artifactId>spring-boot-admin-server-cloud</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>de.codecentric</groupId> |
|||
<artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 --> |
|||
<version>${spring-boot-admin.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.mockito</groupId> |
|||
<artifactId>mockito-inline</artifactId> |
|||
<version>${mockito-inline.version}</version> <!-- 支持 Mockito 的 final 类与 static 方法的 mock --> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<artifactId>asm</artifactId> |
|||
<groupId>org.ow2.asm</groupId> |
|||
</exclusion> |
|||
<exclusion> |
|||
<groupId>org.mockito</groupId> |
|||
<artifactId>mockito-core</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 --> |
|||
<artifactId>jedis-mock</artifactId> |
|||
<version>${jedis-mock.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 --> |
|||
<artifactId>podam</artifactId> |
|||
<version>${podam.version}</version> |
|||
</dependency> |
|||
|
|||
<!-- 工作流相关 --> |
|||
<dependency> |
|||
<groupId>org.flowable</groupId> |
|||
<artifactId>flowable-spring-boot-starter-process</artifactId> |
|||
<version>${flowable.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.flowable</groupId> |
|||
<artifactId>flowable-spring-boot-starter-actuator</artifactId> |
|||
<version>${flowable.version}</version> |
|||
</dependency> |
|||
<!-- 工作流相关结束 --> |
|||
|
|||
<!-- 工具类相关 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-common</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-excel</artifactId> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
<version>${lombok.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.mapstruct</groupId> |
|||
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher --> |
|||
<version>${mapstruct.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.mapstruct</groupId> |
|||
<artifactId>mapstruct-jdk8</artifactId> |
|||
<version>${mapstruct.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.mapstruct</groupId> |
|||
<artifactId>mapstruct-processor</artifactId> |
|||
<version>${mapstruct.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>cn.hutool</groupId> |
|||
<artifactId>hutool-all</artifactId> |
|||
<version>${hutool-5.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.dromara.hutool</groupId> |
|||
<artifactId>hutool-extra</artifactId> |
|||
<version>${hutool-6.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>easyexcel</artifactId> |
|||
<version>${easyexcel.verion}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>commons-io</groupId> |
|||
<artifactId>commons-io</artifactId> |
|||
<version>${commons-io.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.apache.commons</groupId> |
|||
<artifactId>commons-compress</artifactId> |
|||
<version>${commons-compress.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.apache.tika</groupId> |
|||
<artifactId>tika-core</artifactId> <!-- 文件类型的识别 --> |
|||
<version>${tika-core.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.apache.velocity</groupId> |
|||
<artifactId>velocity-engine-core</artifactId> |
|||
<version>${velocity.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>fastjson</artifactId> |
|||
<version>${fastjson.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.google.guava</groupId> |
|||
<artifactId>guava</artifactId> |
|||
<version>${guava.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>transmittable-thread-local</artifactId> <!-- 解决 ThreadLocal 父子线程的传值问题 --> |
|||
<version>${transmittable-thread-local.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>commons-net</groupId> |
|||
<artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 --> |
|||
<version>${commons-net.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.jcraft</groupId> |
|||
<artifactId>jsch</artifactId> <!-- 解决 sftp 连接 --> |
|||
<version>${jsch.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.xingyuv</groupId> |
|||
<artifactId>spring-boot-starter-captcha-plus</artifactId> |
|||
<version>${captcha-plus.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.lionsoul</groupId> |
|||
<artifactId>ip2region</artifactId> |
|||
<version>${ip2region.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.jsoup</groupId> |
|||
<artifactId>jsoup</artifactId> |
|||
<version>${jsoup.version}</version> |
|||
</dependency> |
|||
|
|||
<!-- 三方云服务相关 --> |
|||
<dependency> |
|||
<groupId>com.squareup.okio</groupId> |
|||
<artifactId>okio</artifactId> |
|||
<version>${okio.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.squareup.okhttp3</groupId> |
|||
<artifactId>okhttp</artifactId> |
|||
<version>${okhttp3.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>io.minio</groupId> |
|||
<artifactId>minio</artifactId> |
|||
<version>${minio.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.xingyuv</groupId> |
|||
<artifactId>spring-boot-starter-justauth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) --> |
|||
<version>${justauth.version}</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>cn.hutool</groupId> |
|||
<artifactId>hutool-core</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.github.binarywang</groupId> |
|||
<artifactId>weixin-java-pay</artifactId> |
|||
<version>${weixin-java.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.github.binarywang</groupId> |
|||
<artifactId>wx-java-mp-spring-boot-starter</artifactId> |
|||
<version>${weixin-java.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.github.binarywang</groupId> |
|||
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId> |
|||
<version>${weixin-java.version}</version> |
|||
</dependency> |
|||
|
|||
<!-- 积木报表--> |
|||
<dependency> |
|||
<groupId>org.jeecgframework.jimureport</groupId> |
|||
<artifactId>jimureport-spring-boot3-starter-fastjson2</artifactId> |
|||
<version>${jimureport.version}</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>druid</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<!-- MQTT --> |
|||
<dependency> |
|||
<groupId>org.eclipse.paho</groupId> |
|||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId> |
|||
<version>${mqtt.version}</version> |
|||
</dependency> |
|||
</dependencies> |
|||
</dependencyManagement> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<!-- 统一 revision 版本 --> |
|||
<plugin> |
|||
<groupId>org.codehaus.mojo</groupId> |
|||
<artifactId>flatten-maven-plugin</artifactId> |
|||
<version>${flatten-maven-plugin.version}</version> |
|||
<configuration> |
|||
<flattenMode>bom</flattenMode> |
|||
<updatePomFile>true</updatePomFile> |
|||
</configuration> |
|||
<executions> |
|||
<execution> |
|||
<goals> |
|||
<goal>flatten</goal> |
|||
</goals> |
|||
<id>flatten</id> |
|||
<phase>process-resources</phase> |
|||
</execution> |
|||
<execution> |
|||
<goals> |
|||
<goal>clean</goal> |
|||
</goals> |
|||
<id>flatten.clean</id> |
|||
<phase>clean</phase> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
|
|||
</project> |
@ -0,0 +1,45 @@ |
|||
<?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>win</artifactId> |
|||
<groupId>com.win</groupId> |
|||
<version>${revision}</version> |
|||
</parent> |
|||
<packaging>pom</packaging> |
|||
<modules> |
|||
<module>win-common</module> |
|||
<module>win-spring-boot-starter-mybatis</module> |
|||
<module>win-spring-boot-starter-redis</module> |
|||
<module>win-spring-boot-starter-web</module> |
|||
<module>win-spring-boot-starter-security</module> |
|||
<module>win-spring-boot-starter-websocket</module> |
|||
|
|||
<module>win-spring-boot-starter-monitor</module> |
|||
<module>win-spring-boot-starter-protection</module> |
|||
<module>win-spring-boot-starter-job</module> |
|||
<module>win-spring-boot-starter-mq</module> |
|||
|
|||
<module>win-spring-boot-starter-excel</module> |
|||
|
|||
<module>win-spring-boot-starter-biz-tenant</module> |
|||
<module>win-spring-boot-starter-biz-data-permission</module> |
|||
<module>win-spring-boot-starter-biz-ip</module> |
|||
</modules> |
|||
|
|||
<artifactId>win-framework</artifactId> |
|||
<description> |
|||
该包是技术组件,每个子包,代表一个组件。每个组件包括两部分: |
|||
1. core 包:是该组件的核心封装 |
|||
2. config 包:是该组件基于 Spring 的配置 |
|||
|
|||
技术组件,也分成两类: |
|||
1. 框架组件:和我们熟悉的 MyBatis、Redis 等等的拓展 |
|||
2. 业务组件:和业务相关的组件的封装,例如说数据字典、操作日志等等。 |
|||
如果是业务组件,Maven 名字会包含 biz |
|||
</description> |
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
|||
|
|||
</project> |
@ -0,0 +1,149 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<parent> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-framework</artifactId> |
|||
<version>${revision}</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<artifactId>win-common</artifactId> |
|||
<packaging>jar</packaging> |
|||
|
|||
<name>${project.artifactId}</name> |
|||
<description>定义基础 pojo 类、枚举、工具类等等</description> |
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
|||
|
|||
<dependencies> |
|||
<!-- Spring 核心 --> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-core</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-expression</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-aop</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.aspectj</groupId> |
|||
<artifactId>aspectjweaver</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 --> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-configuration-processor</artifactId> |
|||
<optional>true</optional> |
|||
</dependency> |
|||
|
|||
<!-- Web 相关 --> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-web</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>jakarta.servlet</groupId> |
|||
<artifactId>jakarta.servlet-api</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springdoc</groupId> |
|||
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 --> |
|||
</dependency> |
|||
|
|||
<!-- 监控相关 --> |
|||
<dependency> |
|||
<groupId>org.apache.skywalking</groupId> |
|||
<artifactId>apm-toolkit-trace</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- 工具类相关 --> |
|||
<dependency> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.mapstruct</groupId> |
|||
<artifactId>mapstruct</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.mapstruct</groupId> |
|||
<artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher --> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.mapstruct</groupId> |
|||
<artifactId>mapstruct-processor</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.google.guava</groupId> |
|||
<artifactId>guava</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.fasterxml.jackson.core</groupId> |
|||
<artifactId>jackson-databind</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.fasterxml.jackson.core</groupId> |
|||
<artifactId>jackson-core</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.fasterxml.jackson.datatype</groupId> |
|||
<artifactId>jackson-datatype-jsr310</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.slf4j</groupId> |
|||
<artifactId>slf4j-api</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>jakarta.validation</groupId> |
|||
<artifactId>jakarta.validation-api</artifactId> |
|||
<scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 --> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>cn.hutool</groupId> |
|||
<artifactId>hutool-all</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>transmittable-thread-local</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 --> |
|||
<artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因,方便 xxx-module-api 包使用 --> |
|||
</dependency> |
|||
|
|||
<!-- Test 测试相关 --> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
</dependencies> |
|||
|
|||
</project> |
@ -0,0 +1,15 @@ |
|||
package com.win.framework.common.core; |
|||
|
|||
/** |
|||
* 可生成 Int 数组的接口 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public interface IntArrayValuable { |
|||
|
|||
/** |
|||
* @return int 数组 |
|||
*/ |
|||
int[] array(); |
|||
|
|||
} |
@ -0,0 +1,22 @@ |
|||
package com.win.framework.common.core; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* Key Value 的键值对 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class KeyValue<K, V> implements Serializable { |
|||
|
|||
private K key; |
|||
private V value; |
|||
|
|||
} |
@ -0,0 +1,46 @@ |
|||
package com.win.framework.common.enums; |
|||
|
|||
import cn.hutool.core.util.ObjUtil; |
|||
import com.win.framework.common.core.IntArrayValuable; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* 通用状态枚举 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum CommonStatusEnum implements IntArrayValuable { |
|||
|
|||
ENABLE(0, "开启"), |
|||
DISABLE(1, "关闭"); |
|||
|
|||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray(); |
|||
|
|||
/** |
|||
* 状态值 |
|||
*/ |
|||
private final Integer status; |
|||
/** |
|||
* 状态名 |
|||
*/ |
|||
private final String name; |
|||
|
|||
@Override |
|||
public int[] array() { |
|||
return ARRAYS; |
|||
} |
|||
|
|||
public static boolean isEnable(Integer status) { |
|||
return ObjUtil.equal(ENABLE.status, status); |
|||
} |
|||
|
|||
public static boolean isDisable(Integer status) { |
|||
return ObjUtil.equal(DISABLE.status, status); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,46 @@ |
|||
package com.win.framework.common.enums; |
|||
|
|||
import cn.hutool.core.util.ArrayUtil; |
|||
import com.win.framework.common.core.IntArrayValuable; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* 时间间隔的枚举 |
|||
* |
|||
* @author dhb52 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum DateIntervalEnum implements IntArrayValuable { |
|||
|
|||
DAY(1, "天"), |
|||
WEEK(2, "周"), |
|||
MONTH(3, "月"), |
|||
QUARTER(4, "季度"), |
|||
YEAR(5, "年") |
|||
; |
|||
|
|||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray(); |
|||
|
|||
/** |
|||
* 类型 |
|||
*/ |
|||
private final Integer interval; |
|||
/** |
|||
* 名称 |
|||
*/ |
|||
private final String name; |
|||
|
|||
@Override |
|||
public int[] array() { |
|||
return ARRAYS; |
|||
} |
|||
|
|||
public static DateIntervalEnum valueOf(Integer interval) { |
|||
return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values()); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,21 @@ |
|||
package com.win.framework.common.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* 文档地址 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum DocumentEnum { |
|||
|
|||
REDIS_INSTALL("https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ", "Redis 安装文档"), |
|||
TENANT("https://doc.iocoder.cn", "SaaS 多租户文档"); |
|||
|
|||
private final String url; |
|||
private final String memo; |
|||
|
|||
} |
@ -0,0 +1,40 @@ |
|||
package com.win.framework.common.enums; |
|||
|
|||
import com.win.framework.common.core.IntArrayValuable; |
|||
import lombok.Getter; |
|||
import lombok.RequiredArgsConstructor; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* 终端的枚举 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@RequiredArgsConstructor |
|||
@Getter |
|||
public enum TerminalEnum implements IntArrayValuable { |
|||
|
|||
UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
|
|||
WECHAT_MINI_PROGRAM(10, "微信小程序"), |
|||
WECHAT_WAP(11, "微信公众号"), |
|||
H5(20, "H5 网页"), |
|||
APP(31, "手机 App"), |
|||
; |
|||
|
|||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TerminalEnum::getTerminal).toArray(); |
|||
|
|||
/** |
|||
* 终端 |
|||
*/ |
|||
private final Integer terminal; |
|||
/** |
|||
* 终端名 |
|||
*/ |
|||
private final String name; |
|||
|
|||
@Override |
|||
public int[] array() { |
|||
return ARRAYS; |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
package com.win.framework.common.enums; |
|||
|
|||
import cn.hutool.core.util.ArrayUtil; |
|||
import com.win.framework.common.core.IntArrayValuable; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* 全局用户类型枚举 |
|||
*/ |
|||
@AllArgsConstructor |
|||
@Getter |
|||
public enum UserTypeEnum implements IntArrayValuable { |
|||
|
|||
MEMBER(1, "会员"), // 面向 c 端,普通用户
|
|||
ADMIN(2, "管理员"); // 面向 b 端,管理后台
|
|||
|
|||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray(); |
|||
|
|||
/** |
|||
* 类型 |
|||
*/ |
|||
private final Integer value; |
|||
/** |
|||
* 类型名 |
|||
*/ |
|||
private final String name; |
|||
|
|||
public static UserTypeEnum valueOf(Integer value) { |
|||
return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values()); |
|||
} |
|||
|
|||
@Override |
|||
public int[] array() { |
|||
return ARRAYS; |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
package com.win.framework.common.enums; |
|||
|
|||
/** |
|||
* Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期 |
|||
* |
|||
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enums 包下 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public interface WebFilterOrderEnum { |
|||
|
|||
int CORS_FILTER = Integer.MIN_VALUE; |
|||
|
|||
int TRACE_FILTER = CORS_FILTER + 1; |
|||
|
|||
int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500; |
|||
|
|||
// OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
|
|||
|
|||
int TENANT_CONTEXT_FILTER = - 104; // 需要保证在 ApiAccessLogFilter 前面
|
|||
|
|||
int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
|
|||
|
|||
int XSS_FILTER = -102; // 需要保证在 RequestBodyCacheFilter 后面
|
|||
|
|||
// Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类
|
|||
|
|||
int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面
|
|||
|
|||
int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面
|
|||
|
|||
int DEMO_FILTER = Integer.MAX_VALUE; |
|||
|
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.win.framework.common.exception; |
|||
|
|||
import com.win.framework.common.exception.enums.GlobalErrorCodeConstants; |
|||
import com.win.framework.common.exception.enums.ServiceErrorCodeRange; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* 错误码对象 |
|||
* |
|||
* 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants} |
|||
* 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange} |
|||
* |
|||
* TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 |
|||
*/ |
|||
@Data |
|||
public class ErrorCode { |
|||
|
|||
/** |
|||
* 错误码 |
|||
*/ |
|||
private final Integer code; |
|||
/** |
|||
* 错误提示 |
|||
*/ |
|||
private final String msg; |
|||
|
|||
public ErrorCode(Integer code, String message) { |
|||
this.code = code; |
|||
this.msg = message; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,60 @@ |
|||
package com.win.framework.common.exception; |
|||
|
|||
import com.win.framework.common.exception.enums.GlobalErrorCodeConstants; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
/** |
|||
* 服务器异常 Exception |
|||
*/ |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public final class ServerException extends RuntimeException { |
|||
|
|||
/** |
|||
* 全局错误码 |
|||
* |
|||
* @see GlobalErrorCodeConstants |
|||
*/ |
|||
private Integer code; |
|||
/** |
|||
* 错误提示 |
|||
*/ |
|||
private String message; |
|||
|
|||
/** |
|||
* 空构造方法,避免反序列化问题 |
|||
*/ |
|||
public ServerException() { |
|||
} |
|||
|
|||
public ServerException(ErrorCode errorCode) { |
|||
this.code = errorCode.getCode(); |
|||
this.message = errorCode.getMsg(); |
|||
} |
|||
|
|||
public ServerException(Integer code, String message) { |
|||
this.code = code; |
|||
this.message = message; |
|||
} |
|||
|
|||
public Integer getCode() { |
|||
return code; |
|||
} |
|||
|
|||
public ServerException setCode(Integer code) { |
|||
this.code = code; |
|||
return this; |
|||
} |
|||
|
|||
@Override |
|||
public String getMessage() { |
|||
return message; |
|||
} |
|||
|
|||
public ServerException setMessage(String message) { |
|||
this.message = message; |
|||
return this; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,60 @@ |
|||
package com.win.framework.common.exception; |
|||
|
|||
import com.win.framework.common.exception.enums.ServiceErrorCodeRange; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
/** |
|||
* 业务逻辑异常 Exception |
|||
*/ |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public final class ServiceException extends RuntimeException { |
|||
|
|||
/** |
|||
* 业务错误码 |
|||
* |
|||
* @see ServiceErrorCodeRange |
|||
*/ |
|||
private Integer code; |
|||
/** |
|||
* 错误提示 |
|||
*/ |
|||
private String message; |
|||
|
|||
/** |
|||
* 空构造方法,避免反序列化问题 |
|||
*/ |
|||
public ServiceException() { |
|||
} |
|||
|
|||
public ServiceException(ErrorCode errorCode) { |
|||
this.code = errorCode.getCode(); |
|||
this.message = errorCode.getMsg(); |
|||
} |
|||
|
|||
public ServiceException(Integer code, String message) { |
|||
this.code = code; |
|||
this.message = message; |
|||
} |
|||
|
|||
public Integer getCode() { |
|||
return code; |
|||
} |
|||
|
|||
public ServiceException setCode(Integer code) { |
|||
this.code = code; |
|||
return this; |
|||
} |
|||
|
|||
@Override |
|||
public String getMessage() { |
|||
return message; |
|||
} |
|||
|
|||
public ServiceException setMessage(String message) { |
|||
this.message = message; |
|||
return this; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,41 @@ |
|||
package com.win.framework.common.exception.enums; |
|||
|
|||
import com.win.framework.common.exception.ErrorCode; |
|||
|
|||
/** |
|||
* 全局错误码枚举 |
|||
* 0-999 系统异常编码保留 |
|||
* |
|||
* 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
|
|||
* 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的 |
|||
* 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public interface GlobalErrorCodeConstants { |
|||
|
|||
ErrorCode SUCCESS = new ErrorCode(0, "成功"); |
|||
|
|||
// ========== 客户端错误段 ==========
|
|||
|
|||
ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确"); |
|||
ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录"); |
|||
ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限"); |
|||
ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到"); |
|||
ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确"); |
|||
ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
|
|||
ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试"); |
|||
|
|||
// ========== 服务端错误段 ==========
|
|||
|
|||
ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常"); |
|||
ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启"); |
|||
ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项"); |
|||
|
|||
// ========== 自定义错误段 ==========
|
|||
ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
|
|||
ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作"); |
|||
|
|||
ErrorCode UNKNOWN = new ErrorCode(999, "未知错误"); |
|||
|
|||
} |
@ -0,0 +1,48 @@ |
|||
package com.win.framework.common.exception.enums; |
|||
|
|||
/** |
|||
* 业务异常的错误码区间,解决:解决各模块错误码定义,避免重复,在此只声明不做实际使用 |
|||
* |
|||
* 一共 10 位,分成四段 |
|||
* |
|||
* 第一段,1 位,类型 |
|||
* 1 - 业务级别异常 |
|||
* x - 预留 |
|||
* 第二段,3 位,系统类型 |
|||
* 001 - 用户系统 |
|||
* 002 - 商品系统 |
|||
* 003 - 订单系统 |
|||
* 004 - 支付系统 |
|||
* 005 - 优惠劵系统 |
|||
* ... - ... |
|||
* 第三段,3 位,模块 |
|||
* 不限制规则。 |
|||
* 一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子: |
|||
* 001 - OAuth2 模块 |
|||
* 002 - User 模块 |
|||
* 003 - MobileCode 模块 |
|||
* 第四段,3 位,错误码 |
|||
* 不限制规则。 |
|||
* 一般建议,每个模块自增。 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class ServiceErrorCodeRange { |
|||
|
|||
// 模块 infra 错误码区间 [1-001-000-000 ~ 1-002-000-000)
|
|||
// 模块 system 错误码区间 [1-002-000-000 ~ 1-003-000-000)
|
|||
// 模块 report 错误码区间 [1-003-000-000 ~ 1-004-000-000)
|
|||
// 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)
|
|||
// 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000)
|
|||
// 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)
|
|||
// 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)
|
|||
|
|||
// 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)
|
|||
// 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000)
|
|||
// 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000)
|
|||
|
|||
// 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000)
|
|||
|
|||
// 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000)
|
|||
|
|||
} |
@ -0,0 +1,77 @@ |
|||
package com.win.framework.common.exception.util; |
|||
|
|||
import com.win.framework.common.exception.ErrorCode; |
|||
import com.win.framework.common.exception.ServiceException; |
|||
import com.win.framework.common.exception.enums.GlobalErrorCodeConstants; |
|||
import com.google.common.annotations.VisibleForTesting; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
/** |
|||
* {@link ServiceException} 工具类 |
|||
* |
|||
* 目的在于,格式化异常信息提示。 |
|||
* 考虑到 String.format 在参数不正确时会报错,因此使用 {} 作为占位符,并使用 {@link #doFormat(int, String, Object...)} 方法来格式化 |
|||
* |
|||
*/ |
|||
@Slf4j |
|||
public class ServiceExceptionUtil { |
|||
|
|||
// ========== 和 ServiceException 的集成 ==========
|
|||
|
|||
public static ServiceException exception(ErrorCode errorCode) { |
|||
return exception0(errorCode.getCode(), errorCode.getMsg()); |
|||
} |
|||
|
|||
public static ServiceException exception(ErrorCode errorCode, Object... params) { |
|||
return exception0(errorCode.getCode(), errorCode.getMsg(), params); |
|||
} |
|||
|
|||
public static ServiceException exception0(Integer code, String messagePattern, Object... params) { |
|||
String message = doFormat(code, messagePattern, params); |
|||
return new ServiceException(code, message); |
|||
} |
|||
|
|||
public static ServiceException invalidParamException(String messagePattern, Object... params) { |
|||
return exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), messagePattern, params); |
|||
} |
|||
|
|||
// ========== 格式化方法 ==========
|
|||
|
|||
/** |
|||
* 将错误编号对应的消息使用 params 进行格式化。 |
|||
* |
|||
* @param code 错误编号 |
|||
* @param messagePattern 消息模版 |
|||
* @param params 参数 |
|||
* @return 格式化后的提示 |
|||
*/ |
|||
@VisibleForTesting |
|||
public static String doFormat(int code, String messagePattern, Object... params) { |
|||
StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); |
|||
int i = 0; |
|||
int j; |
|||
int l; |
|||
for (l = 0; l < params.length; l++) { |
|||
j = messagePattern.indexOf("{}", i); |
|||
if (j == -1) { |
|||
log.error("[doFormat][参数过多:错误码({})|错误内容({})|参数({})", code, messagePattern, params); |
|||
if (i == 0) { |
|||
return messagePattern; |
|||
} else { |
|||
sbuf.append(messagePattern.substring(i)); |
|||
return sbuf.toString(); |
|||
} |
|||
} else { |
|||
sbuf.append(messagePattern, i, j); |
|||
sbuf.append(params[l]); |
|||
i = j + 2; |
|||
} |
|||
} |
|||
if (messagePattern.indexOf("{}", i) != -1) { |
|||
log.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params); |
|||
} |
|||
sbuf.append(messagePattern.substring(i)); |
|||
return sbuf.toString(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,6 @@ |
|||
/** |
|||
* 基础的通用类,和框架无关 |
|||
* |
|||
* 例如说,CommonResult 为通用返回 |
|||
*/ |
|||
package com.win.framework.common; |
@ -0,0 +1,112 @@ |
|||
package com.win.framework.common.pojo; |
|||
|
|||
import com.win.framework.common.exception.ErrorCode; |
|||
import com.win.framework.common.exception.ServiceException; |
|||
import com.win.framework.common.exception.enums.GlobalErrorCodeConstants; |
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import lombok.Data; |
|||
import org.springframework.util.Assert; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* 通用返回 |
|||
* |
|||
* @param <T> 数据泛型 |
|||
*/ |
|||
@Data |
|||
public class CommonResult<T> implements Serializable { |
|||
|
|||
/** |
|||
* 错误码 |
|||
* |
|||
* @see ErrorCode#getCode() |
|||
*/ |
|||
private Integer code; |
|||
/** |
|||
* 返回数据 |
|||
*/ |
|||
private T data; |
|||
/** |
|||
* 错误提示,用户可阅读 |
|||
* |
|||
* @see ErrorCode#getMsg() () |
|||
*/ |
|||
private String msg; |
|||
|
|||
/** |
|||
* 将传入的 result 对象,转换成另外一个泛型结果的对象 |
|||
* |
|||
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。 |
|||
* |
|||
* @param result 传入的 result 对象 |
|||
* @param <T> 返回的泛型 |
|||
* @return 新的 CommonResult 对象 |
|||
*/ |
|||
public static <T> CommonResult<T> error(CommonResult<?> result) { |
|||
return error(result.getCode(), result.getMsg()); |
|||
} |
|||
|
|||
public static <T> CommonResult<T> error(Integer code, String message) { |
|||
Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!"); |
|||
CommonResult<T> result = new CommonResult<>(); |
|||
result.code = code; |
|||
result.msg = message; |
|||
return result; |
|||
} |
|||
|
|||
public static <T> CommonResult<T> error(ErrorCode errorCode) { |
|||
return error(errorCode.getCode(), errorCode.getMsg()); |
|||
} |
|||
|
|||
public static <T> CommonResult<T> success(T data) { |
|||
CommonResult<T> result = new CommonResult<>(); |
|||
result.code = GlobalErrorCodeConstants.SUCCESS.getCode(); |
|||
result.data = data; |
|||
result.msg = ""; |
|||
return result; |
|||
} |
|||
|
|||
public static boolean isSuccess(Integer code) { |
|||
return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode()); |
|||
} |
|||
|
|||
@JsonIgnore // 避免 jackson 序列化
|
|||
public boolean isSuccess() { |
|||
return isSuccess(code); |
|||
} |
|||
|
|||
@JsonIgnore // 避免 jackson 序列化
|
|||
public boolean isError() { |
|||
return !isSuccess(); |
|||
} |
|||
|
|||
// ========= 和 Exception 异常体系集成 =========
|
|||
|
|||
/** |
|||
* 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常 |
|||
*/ |
|||
public void checkError() throws ServiceException { |
|||
if (isSuccess()) { |
|||
return; |
|||
} |
|||
// 业务异常
|
|||
throw new ServiceException(code, msg); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常 |
|||
* 如果没有,则返回 {@link #data} 数据 |
|||
*/ |
|||
@JsonIgnore // 避免 jackson 序列化
|
|||
public T getCheckedData() { |
|||
checkError(); |
|||
return data; |
|||
} |
|||
|
|||
public static <T> CommonResult<T> error(ServiceException serviceException) { |
|||
return error(serviceException.getCode(), serviceException.getMessage()); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.win.framework.common.pojo; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
|
|||
import jakarta.validation.constraints.Min; |
|||
import jakarta.validation.constraints.Max; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import java.io.Serializable; |
|||
|
|||
@Schema(description="分页参数") |
|||
@Data |
|||
public class PageParam implements Serializable { |
|||
|
|||
private static final Integer PAGE_NO = 1; |
|||
private static final Integer PAGE_SIZE = 10; |
|||
|
|||
/** |
|||
* 每页条数 - 不分页 |
|||
* |
|||
* 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。 |
|||
*/ |
|||
public static final Integer PAGE_SIZE_NONE = -1; |
|||
|
|||
@Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1") |
|||
@NotNull(message = "页码不能为空") |
|||
@Min(value = 1, message = "页码最小值为 1") |
|||
private Integer pageNo = PAGE_NO; |
|||
|
|||
@Schema(description = "每页条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") |
|||
@NotNull(message = "每页条数不能为空") |
|||
@Min(value = 1, message = "每页条数最小值为 1") |
|||
@Max(value = 100, message = "每页条数最大值为 100") |
|||
private Integer pageSize = PAGE_SIZE; |
|||
|
|||
} |
@ -0,0 +1,41 @@ |
|||
package com.win.framework.common.pojo; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
@Schema(description = "分页结果") |
|||
@Data |
|||
public final class PageResult<T> implements Serializable { |
|||
|
|||
@Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED) |
|||
private List<T> list; |
|||
|
|||
@Schema(description = "总量", requiredMode = Schema.RequiredMode.REQUIRED) |
|||
private Long total; |
|||
|
|||
public PageResult() { |
|||
} |
|||
|
|||
public PageResult(List<T> list, Long total) { |
|||
this.list = list; |
|||
this.total = total; |
|||
} |
|||
|
|||
public PageResult(Long total) { |
|||
this.list = new ArrayList<>(); |
|||
this.total = total; |
|||
} |
|||
|
|||
public static <T> PageResult<T> empty() { |
|||
return new PageResult<>(0L); |
|||
} |
|||
|
|||
public static <T> PageResult<T> empty(Long total) { |
|||
return new PageResult<>(total); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.win.framework.common.pojo; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Schema(description = "可排序的分页参数") |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class SortablePageParam extends PageParam { |
|||
|
|||
@Schema(description = "排序字段") |
|||
private List<SortingField> sortingFields; |
|||
|
|||
} |
@ -0,0 +1,37 @@ |
|||
package com.win.framework.common.pojo; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* 排序字段 DTO |
|||
* |
|||
* 类名加了 ing 的原因是,避免和 ES SortField 重名。 |
|||
*/ |
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class SortingField implements Serializable { |
|||
|
|||
/** |
|||
* 顺序 - 升序 |
|||
*/ |
|||
public static final String ORDER_ASC = "asc"; |
|||
/** |
|||
* 顺序 - 降序 |
|||
*/ |
|||
public static final String ORDER_DESC = "desc"; |
|||
|
|||
/** |
|||
* 字段 |
|||
*/ |
|||
private String field; |
|||
/** |
|||
* 顺序 |
|||
*/ |
|||
private String order; |
|||
|
|||
} |
@ -0,0 +1,49 @@ |
|||
package com.win.framework.common.util.cache; |
|||
|
|||
import com.google.common.cache.CacheBuilder; |
|||
import com.google.common.cache.CacheLoader; |
|||
import com.google.common.cache.LoadingCache; |
|||
|
|||
import java.time.Duration; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
/** |
|||
* Cache 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class CacheUtils { |
|||
|
|||
/** |
|||
* 构建异步刷新的 LoadingCache 对象 |
|||
* |
|||
* 注意:如果你的缓存和 ThreadLocal 有关系,要么自己处理 ThreadLocal 的传递,要么使用 {@link #buildCache(Duration, CacheLoader)} 方法 |
|||
* |
|||
* 或者简单理解: |
|||
* 1、和“人”相关的,使用 {@link #buildCache(Duration, CacheLoader)} 方法 |
|||
* 2、和“全局”、“系统”相关的,使用当前缓存方法 |
|||
* |
|||
* @param duration 过期时间 |
|||
* @param loader CacheLoader 对象 |
|||
* @return LoadingCache 对象 |
|||
*/ |
|||
public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) { |
|||
return CacheBuilder.newBuilder() |
|||
// 只阻塞当前数据加载线程,其他线程返回旧值
|
|||
.refreshAfterWrite(duration) |
|||
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
|
|||
.build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置
|
|||
} |
|||
|
|||
/** |
|||
* 构建同步刷新的 LoadingCache 对象 |
|||
* |
|||
* @param duration 过期时间 |
|||
* @param loader CacheLoader 对象 |
|||
* @return LoadingCache 对象 |
|||
*/ |
|||
public static <K, V> LoadingCache<K, V> buildCache(Duration duration, CacheLoader<K, V> loader) { |
|||
return CacheBuilder.newBuilder().refreshAfterWrite(duration).build(loader); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,58 @@ |
|||
package com.win.framework.common.util.collection; |
|||
|
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
import cn.hutool.core.collection.IterUtil; |
|||
import cn.hutool.core.util.ArrayUtil; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.function.Consumer; |
|||
import java.util.function.Function; |
|||
|
|||
import static com.win.framework.common.util.collection.CollectionUtils.convertList; |
|||
|
|||
/** |
|||
* Array 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class ArrayUtils { |
|||
|
|||
/** |
|||
* 将 object 和 newElements 合并成一个数组 |
|||
* |
|||
* @param object 对象 |
|||
* @param newElements 数组 |
|||
* @param <T> 泛型 |
|||
* @return 结果数组 |
|||
*/ |
|||
@SafeVarargs |
|||
public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) { |
|||
if (object == null) { |
|||
return newElements; |
|||
} |
|||
Consumer<T>[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length); |
|||
result[0] = object; |
|||
System.arraycopy(newElements, 0, result, 1, newElements.length); |
|||
return result; |
|||
} |
|||
|
|||
public static <T, V> V[] toArray(Collection<T> from, Function<T, V> mapper) { |
|||
return toArray(convertList(from, mapper)); |
|||
} |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
public static <T> T[] toArray(Collection<T> from) { |
|||
if (CollectionUtil.isEmpty(from)) { |
|||
return (T[]) (new Object[0]); |
|||
} |
|||
return ArrayUtil.toArray(from, (Class<T>) IterUtil.getElementType(from.iterator())); |
|||
} |
|||
|
|||
public static <T> T get(T[] array, int index) { |
|||
if (null == array || index >= array.length) { |
|||
return null; |
|||
} |
|||
return array[index]; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,330 @@ |
|||
package com.win.framework.common.util.collection; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
import cn.hutool.core.util.ArrayUtil; |
|||
import com.google.common.collect.ImmutableMap; |
|||
|
|||
import java.util.*; |
|||
import java.util.function.*; |
|||
import java.util.stream.Collectors; |
|||
import java.util.stream.Stream; |
|||
|
|||
import static java.util.Arrays.asList; |
|||
|
|||
/** |
|||
* Collection 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class CollectionUtils { |
|||
|
|||
public static boolean containsAny(Object source, Object... targets) { |
|||
return asList(targets).contains(source); |
|||
} |
|||
|
|||
public static boolean isAnyEmpty(Collection<?>... collections) { |
|||
return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty); |
|||
} |
|||
|
|||
public static <T> boolean anyMatch(Collection<T> from, Predicate<T> predicate) { |
|||
return from.stream().anyMatch(predicate); |
|||
} |
|||
|
|||
public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new ArrayList<>(); |
|||
} |
|||
return from.stream().filter(predicate).collect(Collectors.toList()); |
|||
} |
|||
|
|||
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new ArrayList<>(); |
|||
} |
|||
return distinct(from, keyMapper, (t1, t2) -> t1); |
|||
} |
|||
|
|||
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new ArrayList<>(); |
|||
} |
|||
return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values()); |
|||
} |
|||
|
|||
public static <T, U> List<U> convertList(T[] from, Function<T, U> func) { |
|||
if (ArrayUtil.isEmpty(from)) { |
|||
return new ArrayList<>(); |
|||
} |
|||
return convertList(Arrays.asList(from), func); |
|||
} |
|||
|
|||
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new ArrayList<>(); |
|||
} |
|||
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList()); |
|||
} |
|||
|
|||
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new ArrayList<>(); |
|||
} |
|||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList()); |
|||
} |
|||
|
|||
public static <T, U> List<U> convertListByFlatMap(Collection<T> from, |
|||
Function<T, ? extends Stream<? extends U>> func) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new ArrayList<>(); |
|||
} |
|||
return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); |
|||
} |
|||
|
|||
public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from, |
|||
Function<? super T, ? extends U> mapper, |
|||
Function<U, ? extends Stream<? extends R>> func) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new ArrayList<>(); |
|||
} |
|||
return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); |
|||
} |
|||
|
|||
public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) { |
|||
return map.values() |
|||
.stream() |
|||
.flatMap(List::stream) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
|
|||
public static <T> Set<T> convertSet(Collection<T> from) { |
|||
return convertSet(from, v -> v); |
|||
} |
|||
|
|||
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashSet<>(); |
|||
} |
|||
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet()); |
|||
} |
|||
|
|||
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashSet<>(); |
|||
} |
|||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet()); |
|||
} |
|||
|
|||
public static <T, K> Map<K, T> convertMapByFilter(Collection<T> from, Predicate<T> filter, Function<T, K> keyFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashMap<>(); |
|||
} |
|||
return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v)); |
|||
} |
|||
|
|||
public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from, |
|||
Function<T, ? extends Stream<? extends U>> func) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashSet<>(); |
|||
} |
|||
return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); |
|||
} |
|||
|
|||
public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from, |
|||
Function<? super T, ? extends U> mapper, |
|||
Function<U, ? extends Stream<? extends R>> func) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashSet<>(); |
|||
} |
|||
return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); |
|||
} |
|||
|
|||
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashMap<>(); |
|||
} |
|||
return convertMap(from, keyFunc, Function.identity()); |
|||
} |
|||
|
|||
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return supplier.get(); |
|||
} |
|||
return convertMap(from, keyFunc, Function.identity(), supplier); |
|||
} |
|||
|
|||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashMap<>(); |
|||
} |
|||
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1); |
|||
} |
|||
|
|||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashMap<>(); |
|||
} |
|||
return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new); |
|||
} |
|||
|
|||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return supplier.get(); |
|||
} |
|||
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier); |
|||
} |
|||
|
|||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashMap<>(); |
|||
} |
|||
return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier)); |
|||
} |
|||
|
|||
public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashMap<>(); |
|||
} |
|||
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList()))); |
|||
} |
|||
|
|||
public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashMap<>(); |
|||
} |
|||
return from.stream() |
|||
.collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList()))); |
|||
} |
|||
|
|||
// 暂时没想好名字,先以 2 结尾噶
|
|||
public static <T, K, V> Map<K, Set<V>> convertMultiMap2(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return new HashMap<>(); |
|||
} |
|||
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet()))); |
|||
} |
|||
|
|||
public static <T, K> Map<K, T> convertImmutableMap(Collection<T> from, Function<T, K> keyFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return Collections.emptyMap(); |
|||
} |
|||
ImmutableMap.Builder<K, T> builder = ImmutableMap.builder(); |
|||
from.forEach(item -> builder.put(keyFunc.apply(item), item)); |
|||
return builder.build(); |
|||
} |
|||
|
|||
/** |
|||
* 对比老、新两个列表,找出新增、修改、删除的数据 |
|||
* |
|||
* @param oldList 老列表 |
|||
* @param newList 新列表 |
|||
* @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同 |
|||
* 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据 |
|||
* @return [新增列表、修改列表、删除列表] |
|||
*/ |
|||
public static <T> List<List<T>> diffList(Collection<T> oldList, Collection<T> newList, |
|||
BiFunction<T, T, Boolean> sameFunc) { |
|||
List<T> createList = new LinkedList<>(newList); // 默认都认为是新增的,后续会进行移除
|
|||
List<T> updateList = new ArrayList<>(); |
|||
List<T> deleteList = new ArrayList<>(); |
|||
|
|||
// 通过以 oldList 为主遍历,找出 updateList 和 deleteList
|
|||
for (T oldObj : oldList) { |
|||
// 1. 寻找是否有匹配的
|
|||
T foundObj = null; |
|||
for (Iterator<T> iterator = createList.iterator(); iterator.hasNext(); ) { |
|||
T newObj = iterator.next(); |
|||
// 1.1 不匹配,则直接跳过
|
|||
if (!sameFunc.apply(oldObj, newObj)) { |
|||
continue; |
|||
} |
|||
// 1.2 匹配,则移除,并结束寻找
|
|||
iterator.remove(); |
|||
foundObj = newObj; |
|||
break; |
|||
} |
|||
// 2. 匹配添加到 updateList;不匹配则添加到 deleteList 中
|
|||
if (foundObj != null) { |
|||
updateList.add(foundObj); |
|||
} else { |
|||
deleteList.add(oldObj); |
|||
} |
|||
} |
|||
return asList(createList, updateList, deleteList); |
|||
} |
|||
|
|||
public static boolean containsAny(Collection<?> source, Collection<?> candidates) { |
|||
return org.springframework.util.CollectionUtils.containsAny(source, candidates); |
|||
} |
|||
|
|||
public static <T> T getFirst(List<T> from) { |
|||
return !CollectionUtil.isEmpty(from) ? from.get(0) : null; |
|||
} |
|||
|
|||
public static <T> T findFirst(Collection<T> from, Predicate<T> predicate) { |
|||
return findFirst(from, predicate, Function.identity()); |
|||
} |
|||
|
|||
public static <T, U> U findFirst(Collection<T> from, Predicate<T> predicate, Function<T, U> func) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return null; |
|||
} |
|||
return from.stream().filter(predicate).findFirst().map(func).orElse(null); |
|||
} |
|||
|
|||
public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return null; |
|||
} |
|||
assert !from.isEmpty(); // 断言,避免告警
|
|||
T t = from.stream().max(Comparator.comparing(valueFunc)).get(); |
|||
return valueFunc.apply(t); |
|||
} |
|||
|
|||
public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return null; |
|||
} |
|||
assert from.size() > 0; // 断言,避免告警
|
|||
T t = from.stream().min(Comparator.comparing(valueFunc)).get(); |
|||
return valueFunc.apply(t); |
|||
} |
|||
|
|||
public static <T, V extends Comparable<? super V>> T getMinObject(List<T> from, Function<T, V> valueFunc) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return null; |
|||
} |
|||
assert from.size() > 0; // 断言,避免告警
|
|||
return from.stream().min(Comparator.comparing(valueFunc)).get(); |
|||
} |
|||
|
|||
public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc, |
|||
BinaryOperator<V> accumulator) { |
|||
return getSumValue(from, valueFunc, accumulator, null); |
|||
} |
|||
|
|||
public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc, |
|||
BinaryOperator<V> accumulator, V defaultValue) { |
|||
if (CollUtil.isEmpty(from)) { |
|||
return defaultValue; |
|||
} |
|||
assert !from.isEmpty(); // 断言,避免告警
|
|||
return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue); |
|||
} |
|||
|
|||
public static <T> void addIfNotNull(Collection<T> coll, T item) { |
|||
if (item == null) { |
|||
return; |
|||
} |
|||
coll.add(item); |
|||
} |
|||
|
|||
public static <T> Collection<T> singleton(T obj) { |
|||
return obj == null ? Collections.emptyList() : Collections.singleton(obj); |
|||
} |
|||
|
|||
public static <T> List<T> newArrayList(List<List<T>> list) { |
|||
return list.stream().flatMap(Collection::stream).collect(Collectors.toList()); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,68 @@ |
|||
package com.win.framework.common.util.collection; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
import cn.hutool.core.util.ObjUtil; |
|||
import com.win.framework.common.core.KeyValue; |
|||
import com.google.common.collect.Maps; |
|||
import com.google.common.collect.Multimap; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.function.Consumer; |
|||
|
|||
/** |
|||
* Map 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class MapUtils { |
|||
|
|||
/** |
|||
* 从哈希表表中,获得 keys 对应的所有 value 数组 |
|||
* |
|||
* @param multimap 哈希表 |
|||
* @param keys keys |
|||
* @return value 数组 |
|||
*/ |
|||
public static <K, V> List<V> getList(Multimap<K, V> multimap, Collection<K> keys) { |
|||
List<V> result = new ArrayList<>(); |
|||
keys.forEach(k -> { |
|||
Collection<V> values = multimap.get(k); |
|||
if (CollectionUtil.isEmpty(values)) { |
|||
return; |
|||
} |
|||
result.addAll(values); |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 从哈希表查找到 key 对应的 value,然后进一步处理 |
|||
* key 为 null 时, 不处理 |
|||
* 注意,如果查找到的 value 为 null 时,不进行处理 |
|||
* |
|||
* @param map 哈希表 |
|||
* @param key key |
|||
* @param consumer 进一步处理的逻辑 |
|||
*/ |
|||
public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) { |
|||
if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) { |
|||
return; |
|||
} |
|||
V value = map.get(key); |
|||
if (value == null) { |
|||
return; |
|||
} |
|||
consumer.accept(value); |
|||
} |
|||
|
|||
public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) { |
|||
Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size()); |
|||
keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue())); |
|||
return map; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.win.framework.common.util.collection; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
|
|||
import java.util.Set; |
|||
|
|||
/** |
|||
* Set 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class SetUtils { |
|||
|
|||
@SafeVarargs |
|||
public static <T> Set<T> asSet(T... objs) { |
|||
return CollUtil.newHashSet(objs); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,149 @@ |
|||
package com.win.framework.common.util.date; |
|||
|
|||
import cn.hutool.core.date.LocalDateTimeUtil; |
|||
|
|||
import java.time.*; |
|||
import java.util.Calendar; |
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* 时间工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class DateUtils { |
|||
|
|||
/** |
|||
* 时区 - 默认 |
|||
*/ |
|||
public static final String TIME_ZONE_DEFAULT = "GMT+8"; |
|||
|
|||
/** |
|||
* 秒转换成毫秒 |
|||
*/ |
|||
public static final long SECOND_MILLIS = 1000; |
|||
|
|||
public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd"; |
|||
|
|||
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; |
|||
|
|||
/** |
|||
* 将 LocalDateTime 转换成 Date |
|||
* |
|||
* @param date LocalDateTime |
|||
* @return LocalDateTime |
|||
*/ |
|||
public static Date of(LocalDateTime date) { |
|||
if (date == null) { |
|||
return null; |
|||
} |
|||
// 将此日期时间与时区相结合以创建 ZonedDateTime
|
|||
ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault()); |
|||
// 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
|
|||
Instant instant = zonedDateTime.toInstant(); |
|||
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
|
|||
return Date.from(instant); |
|||
} |
|||
|
|||
/** |
|||
* 将 Date 转换成 LocalDateTime |
|||
* |
|||
* @param date Date |
|||
* @return LocalDateTime |
|||
*/ |
|||
public static LocalDateTime of(Date date) { |
|||
if (date == null) { |
|||
return null; |
|||
} |
|||
// 转为时间戳
|
|||
Instant instant = date.toInstant(); |
|||
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
|
|||
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); |
|||
} |
|||
|
|||
public static Date addTime(Duration duration) { |
|||
return new Date(System.currentTimeMillis() + duration.toMillis()); |
|||
} |
|||
|
|||
public static boolean isExpired(LocalDateTime time) { |
|||
LocalDateTime now = LocalDateTime.now(); |
|||
return now.isAfter(time); |
|||
} |
|||
|
|||
/** |
|||
* 创建指定时间 |
|||
* |
|||
* @param year 年 |
|||
* @param mouth 月 |
|||
* @param day 日 |
|||
* @return 指定时间 |
|||
*/ |
|||
public static Date buildTime(int year, int mouth, int day) { |
|||
return buildTime(year, mouth, day, 0, 0, 0); |
|||
} |
|||
|
|||
/** |
|||
* 创建指定时间 |
|||
* |
|||
* @param year 年 |
|||
* @param mouth 月 |
|||
* @param day 日 |
|||
* @param hour 小时 |
|||
* @param minute 分钟 |
|||
* @param second 秒 |
|||
* @return 指定时间 |
|||
*/ |
|||
public static Date buildTime(int year, int mouth, int day, |
|||
int hour, int minute, int second) { |
|||
Calendar calendar = Calendar.getInstance(); |
|||
calendar.set(Calendar.YEAR, year); |
|||
calendar.set(Calendar.MONTH, mouth - 1); |
|||
calendar.set(Calendar.DAY_OF_MONTH, day); |
|||
calendar.set(Calendar.HOUR_OF_DAY, hour); |
|||
calendar.set(Calendar.MINUTE, minute); |
|||
calendar.set(Calendar.SECOND, second); |
|||
calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒
|
|||
return calendar.getTime(); |
|||
} |
|||
|
|||
public static Date max(Date a, Date b) { |
|||
if (a == null) { |
|||
return b; |
|||
} |
|||
if (b == null) { |
|||
return a; |
|||
} |
|||
return a.compareTo(b) > 0 ? a : b; |
|||
} |
|||
|
|||
public static LocalDateTime max(LocalDateTime a, LocalDateTime b) { |
|||
if (a == null) { |
|||
return b; |
|||
} |
|||
if (b == null) { |
|||
return a; |
|||
} |
|||
return a.isAfter(b) ? a : b; |
|||
} |
|||
|
|||
/** |
|||
* 是否今天 |
|||
* |
|||
* @param date 日期 |
|||
* @return 是否 |
|||
*/ |
|||
public static boolean isToday(LocalDateTime date) { |
|||
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now()); |
|||
} |
|||
|
|||
/** |
|||
* 是否昨天 |
|||
* |
|||
* @param date 日期 |
|||
* @return 是否 |
|||
*/ |
|||
public static boolean isYesterday(LocalDateTime date) { |
|||
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1)); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,309 @@ |
|||
package com.win.framework.common.util.date; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.date.DatePattern; |
|||
import cn.hutool.core.date.LocalDateTimeUtil; |
|||
import cn.hutool.core.lang.Assert; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.win.framework.common.enums.DateIntervalEnum; |
|||
|
|||
import java.time.*; |
|||
import java.time.format.DateTimeParseException; |
|||
import java.time.temporal.ChronoUnit; |
|||
import java.time.temporal.TemporalAdjusters; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 时间工具类,用于 {@link java.time.LocalDateTime} |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class LocalDateTimeUtils { |
|||
|
|||
/** |
|||
* 空的 LocalDateTime 对象,主要用于 DB 唯一索引的默认值 |
|||
*/ |
|||
public static LocalDateTime EMPTY = buildTime(1970, 1, 1); |
|||
|
|||
/** |
|||
* 解析时间 |
|||
* |
|||
* 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说,会尽量去解析,直到成功 |
|||
* |
|||
* @param time 时间 |
|||
* @return 时间字符串 |
|||
*/ |
|||
public static LocalDateTime parse(String time) { |
|||
try { |
|||
return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN); |
|||
} catch (DateTimeParseException e) { |
|||
return LocalDateTimeUtil.parse(time); |
|||
} |
|||
} |
|||
|
|||
public static LocalDateTime addTime(Duration duration) { |
|||
return LocalDateTime.now().plus(duration); |
|||
} |
|||
|
|||
public static LocalDateTime minusTime(Duration duration) { |
|||
return LocalDateTime.now().minus(duration); |
|||
} |
|||
|
|||
public static boolean beforeNow(LocalDateTime date) { |
|||
return date.isBefore(LocalDateTime.now()); |
|||
} |
|||
|
|||
public static boolean afterNow(LocalDateTime date) { |
|||
return date.isAfter(LocalDateTime.now()); |
|||
} |
|||
|
|||
/** |
|||
* 创建指定时间 |
|||
* |
|||
* @param year 年 |
|||
* @param mouth 月 |
|||
* @param day 日 |
|||
* @return 指定时间 |
|||
*/ |
|||
public static LocalDateTime buildTime(int year, int mouth, int day) { |
|||
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)}; |
|||
} |
|||
|
|||
/** |
|||
* 判指定断时间,是否在该时间范围内 |
|||
* |
|||
* @param startTime 开始时间 |
|||
* @param endTime 结束时间 |
|||
* @param time 指定时间 |
|||
* @return 是否 |
|||
*/ |
|||
public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) { |
|||
if (startTime == null || endTime == null || time == null) { |
|||
return false; |
|||
} |
|||
return LocalDateTimeUtil.isIn(parse(time), startTime, endTime); |
|||
} |
|||
|
|||
/** |
|||
* 判断当前时间是否在该时间范围内 |
|||
* |
|||
* @param startTime 开始时间 |
|||
* @param endTime 结束时间 |
|||
* @return 是否 |
|||
*/ |
|||
public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) { |
|||
if (startTime == null || endTime == null) { |
|||
return false; |
|||
} |
|||
return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime); |
|||
} |
|||
|
|||
/** |
|||
* 判断当前时间是否在该时间范围内 |
|||
* |
|||
* @param startTime 开始时间 |
|||
* @param endTime 结束时间 |
|||
* @return 是否 |
|||
*/ |
|||
public static boolean isBetween(String startTime, String endTime) { |
|||
if (startTime == null || endTime == null) { |
|||
return false; |
|||
} |
|||
LocalDate nowDate = LocalDate.now(); |
|||
return LocalDateTimeUtil.isIn(LocalDateTime.now(), |
|||
LocalDateTime.of(nowDate, LocalTime.parse(startTime)), |
|||
LocalDateTime.of(nowDate, LocalTime.parse(endTime))); |
|||
} |
|||
|
|||
/** |
|||
* 判断时间段是否重叠 |
|||
* |
|||
* @param startTime1 开始 time1 |
|||
* @param endTime1 结束 time1 |
|||
* @param startTime2 开始 time2 |
|||
* @param endTime2 结束 time2 |
|||
* @return 重叠:true 不重叠:false |
|||
*/ |
|||
public static boolean isOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) { |
|||
LocalDate nowDate = LocalDate.now(); |
|||
return LocalDateTimeUtil.isOverlap(LocalDateTime.of(nowDate, startTime1), LocalDateTime.of(nowDate, endTime1), |
|||
LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2)); |
|||
} |
|||
|
|||
/** |
|||
* 获取指定日期所在的月份的开始时间 |
|||
* 例如:2023-09-30 00:00:00,000 |
|||
* |
|||
* @param date 日期 |
|||
* @return 月份的开始时间 |
|||
*/ |
|||
public static LocalDateTime beginOfMonth(LocalDateTime date) { |
|||
return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN); |
|||
} |
|||
|
|||
/** |
|||
* 获取指定日期所在的月份的最后时间 |
|||
* 例如:2023-09-30 23:59:59,999 |
|||
* |
|||
* @param date 日期 |
|||
* @return 月份的结束时间 |
|||
*/ |
|||
public static LocalDateTime endOfMonth(LocalDateTime date) { |
|||
return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX); |
|||
} |
|||
|
|||
/** |
|||
* 获得指定日期所在季度 |
|||
* |
|||
* @param date 日期 |
|||
* @return 所在季度 |
|||
*/ |
|||
public static int getQuarterOfYear(LocalDateTime date) { |
|||
return (date.getMonthValue() - 1) / 3 + 1; |
|||
} |
|||
|
|||
/** |
|||
* 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负 |
|||
* |
|||
* @param dateTime 日期 |
|||
* @return 相差天数 |
|||
*/ |
|||
public static Long between(LocalDateTime dateTime) { |
|||
return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS); |
|||
} |
|||
|
|||
/** |
|||
* 获取今天的开始时间 |
|||
* |
|||
* @return 今天 |
|||
*/ |
|||
public static LocalDateTime getToday() { |
|||
return LocalDateTimeUtil.beginOfDay(LocalDateTime.now()); |
|||
} |
|||
|
|||
/** |
|||
* 获取昨天的开始时间 |
|||
* |
|||
* @return 昨天 |
|||
*/ |
|||
public static LocalDateTime getYesterday() { |
|||
return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1)); |
|||
} |
|||
|
|||
/** |
|||
* 获取本月的开始时间 |
|||
* |
|||
* @return 本月 |
|||
*/ |
|||
public static LocalDateTime getMonth() { |
|||
return beginOfMonth(LocalDateTime.now()); |
|||
} |
|||
|
|||
/** |
|||
* 获取本年的开始时间 |
|||
* |
|||
* @return 本年 |
|||
*/ |
|||
public static LocalDateTime getYear() { |
|||
return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN); |
|||
} |
|||
|
|||
public static List<LocalDateTime[]> getDateRangeList(LocalDateTime startTime, |
|||
LocalDateTime endTime, |
|||
Integer interval) { |
|||
// 1.1 找到枚举
|
|||
DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval); |
|||
Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval); |
|||
// 1.2 将时间对齐
|
|||
startTime = LocalDateTimeUtil.beginOfDay(startTime); |
|||
endTime = LocalDateTimeUtil.endOfDay(endTime); |
|||
|
|||
// 2. 循环,生成时间范围
|
|||
List<LocalDateTime[]> timeRanges = new ArrayList<>(); |
|||
switch (intervalEnum) { |
|||
case DAY: |
|||
while (startTime.isBefore(endTime)) { |
|||
timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)}); |
|||
startTime = startTime.plusDays(1); |
|||
} |
|||
break; |
|||
case WEEK: |
|||
while (startTime.isBefore(endTime)) { |
|||
LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1); |
|||
timeRanges.add(new LocalDateTime[]{startTime, endOfWeek}); |
|||
startTime = endOfWeek.plusNanos(1); |
|||
} |
|||
break; |
|||
case MONTH: |
|||
while (startTime.isBefore(endTime)) { |
|||
LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1); |
|||
timeRanges.add(new LocalDateTime[]{startTime, endOfMonth}); |
|||
startTime = endOfMonth.plusNanos(1); |
|||
} |
|||
break; |
|||
case QUARTER: |
|||
while (startTime.isBefore(endTime)) { |
|||
int quarterOfYear = getQuarterOfYear(startTime); |
|||
LocalDateTime quarterEnd = quarterOfYear == 4 |
|||
? startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1) |
|||
: startTime.withMonth(quarterOfYear * 3 + 1).withDayOfMonth(1).minusNanos(1); |
|||
timeRanges.add(new LocalDateTime[]{startTime, quarterEnd}); |
|||
startTime = quarterEnd.plusNanos(1); |
|||
} |
|||
break; |
|||
case YEAR: |
|||
while (startTime.isBefore(endTime)) { |
|||
LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1); |
|||
timeRanges.add(new LocalDateTime[]{startTime, endOfYear}); |
|||
startTime = endOfYear.plusNanos(1); |
|||
} |
|||
break; |
|||
default: |
|||
throw new IllegalArgumentException("Invalid interval: " + interval); |
|||
} |
|||
// 3. 兜底,最后一个时间,需要保持在 endTime 之前
|
|||
LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges); |
|||
if (lastTimeRange != null) { |
|||
lastTimeRange[1] = endTime; |
|||
} |
|||
return timeRanges; |
|||
} |
|||
|
|||
/** |
|||
* 格式化时间范围 |
|||
* |
|||
* @param startTime 开始时间 |
|||
* @param endTime 结束时间 |
|||
* @param interval 时间间隔 |
|||
* @return 时间范围 |
|||
*/ |
|||
public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) { |
|||
// 1. 找到枚举
|
|||
DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval); |
|||
Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval); |
|||
|
|||
// 2. 循环,生成时间范围
|
|||
switch (intervalEnum) { |
|||
case DAY: |
|||
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN); |
|||
case WEEK: |
|||
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN) |
|||
+ StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime)); |
|||
case MONTH: |
|||
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN); |
|||
case QUARTER: |
|||
return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime)); |
|||
case YEAR: |
|||
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN); |
|||
default: |
|||
throw new IllegalArgumentException("Invalid interval: " + interval); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,163 @@ |
|||
package com.win.framework.common.util.http; |
|||
|
|||
import cn.hutool.core.codec.Base64; |
|||
import cn.hutool.core.map.TableMap; |
|||
import cn.hutool.core.net.url.UrlBuilder; |
|||
import cn.hutool.core.util.ReflectUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import cn.hutool.http.HttpRequest; |
|||
import cn.hutool.http.HttpResponse; |
|||
import org.springframework.util.StringUtils; |
|||
import org.springframework.web.util.UriComponents; |
|||
import org.springframework.web.util.UriComponentsBuilder; |
|||
|
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import java.net.URI; |
|||
import java.nio.charset.Charset; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* HTTP 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class HttpUtils { |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
public static String replaceUrlQuery(String url, String key, String value) { |
|||
UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset()); |
|||
// 先移除
|
|||
TableMap<CharSequence, CharSequence> query = (TableMap<CharSequence, CharSequence>) |
|||
ReflectUtil.getFieldValue(builder.getQuery(), "query"); |
|||
query.remove(key); |
|||
// 后添加
|
|||
builder.addQuery(key, value); |
|||
return builder.build(); |
|||
} |
|||
|
|||
private String append(String base, Map<String, ?> query, boolean fragment) { |
|||
return append(base, query, null, fragment); |
|||
} |
|||
|
|||
/** |
|||
* 拼接 URL |
|||
* |
|||
* copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法 |
|||
* |
|||
* @param base 基础 URL |
|||
* @param query 查询参数 |
|||
* @param keys query 的 key,对应的原本的 key 的映射。例如说 query 里有个 key 是 xx,实际它的 key 是 extra_xx,则通过 keys 里添加这个映射 |
|||
* @param fragment URL 的 fragment,即拼接到 # 中 |
|||
* @return 拼接后的 URL |
|||
*/ |
|||
public static String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) { |
|||
UriComponentsBuilder template = UriComponentsBuilder.newInstance(); |
|||
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base); |
|||
URI redirectUri; |
|||
try { |
|||
// assume it's encoded to start with (if it came in over the wire)
|
|||
redirectUri = builder.build(true).toUri(); |
|||
} catch (Exception e) { |
|||
// ... but allow client registrations to contain hard-coded non-encoded values
|
|||
redirectUri = builder.build().toUri(); |
|||
builder = UriComponentsBuilder.fromUri(redirectUri); |
|||
} |
|||
template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost()) |
|||
.userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath()); |
|||
|
|||
if (fragment) { |
|||
StringBuilder values = new StringBuilder(); |
|||
if (redirectUri.getFragment() != null) { |
|||
String append = redirectUri.getFragment(); |
|||
values.append(append); |
|||
} |
|||
for (String key : query.keySet()) { |
|||
if (values.length() > 0) { |
|||
values.append("&"); |
|||
} |
|||
String name = key; |
|||
if (keys != null && keys.containsKey(key)) { |
|||
name = keys.get(key); |
|||
} |
|||
values.append(name).append("={").append(key).append("}"); |
|||
} |
|||
if (values.length() > 0) { |
|||
template.fragment(values.toString()); |
|||
} |
|||
UriComponents encoded = template.build().expand(query).encode(); |
|||
builder.fragment(encoded.getFragment()); |
|||
} else { |
|||
for (String key : query.keySet()) { |
|||
String name = key; |
|||
if (keys != null && keys.containsKey(key)) { |
|||
name = keys.get(key); |
|||
} |
|||
template.queryParam(name, "{" + key + "}"); |
|||
} |
|||
template.fragment(redirectUri.getFragment()); |
|||
UriComponents encoded = template.build().expand(query).encode(); |
|||
builder.query(encoded.getQuery()); |
|||
} |
|||
return builder.build().toUriString(); |
|||
} |
|||
|
|||
public static String[] obtainBasicAuthorization(HttpServletRequest request) { |
|||
String clientId; |
|||
String clientSecret; |
|||
// 先从 Header 中获取
|
|||
String authorization = request.getHeader("Authorization"); |
|||
authorization = StrUtil.subAfter(authorization, "Basic ", true); |
|||
if (StringUtils.hasText(authorization)) { |
|||
authorization = Base64.decodeStr(authorization); |
|||
clientId = StrUtil.subBefore(authorization, ":", false); |
|||
clientSecret = StrUtil.subAfter(authorization, ":", false); |
|||
// 再从 Param 中获取
|
|||
} else { |
|||
clientId = request.getParameter("client_id"); |
|||
clientSecret = request.getParameter("client_secret"); |
|||
} |
|||
|
|||
// 如果两者非空,则返回
|
|||
if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) { |
|||
return new String[]{clientId, clientSecret}; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* HTTP post 请求,基于 {@link cn.hutool.http.HttpUtil} 实现 |
|||
* |
|||
* 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数 |
|||
* |
|||
* @param url URL |
|||
* @param headers 请求头 |
|||
* @param requestBody 请求体 |
|||
* @return 请求结果 |
|||
*/ |
|||
public static String post(String url, Map<String, String> headers, String requestBody) { |
|||
try (HttpResponse response = HttpRequest.post(url) |
|||
.addHeaders(headers) |
|||
.body(requestBody) |
|||
.execute()) { |
|||
return response.body(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* HTTP get 请求,基于 {@link cn.hutool.http.HttpUtil} 实现 |
|||
* |
|||
* 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数 |
|||
* |
|||
* @param url URL |
|||
* @param headers 请求头 |
|||
* @return 请求结果 |
|||
*/ |
|||
public static String get(String url, Map<String, String> headers) { |
|||
try (HttpResponse response = HttpRequest.get(url) |
|||
.addHeaders(headers) |
|||
.execute()) { |
|||
return response.body(); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,84 @@ |
|||
package com.win.framework.common.util.io; |
|||
|
|||
import cn.hutool.core.io.FileTypeUtil; |
|||
import cn.hutool.core.io.FileUtil; |
|||
import cn.hutool.core.io.file.FileNameUtil; |
|||
import cn.hutool.core.util.IdUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import cn.hutool.crypto.digest.DigestUtil; |
|||
import lombok.SneakyThrows; |
|||
|
|||
import java.io.ByteArrayInputStream; |
|||
import java.io.File; |
|||
|
|||
/** |
|||
* 文件工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class FileUtils { |
|||
|
|||
/** |
|||
* 创建临时文件 |
|||
* 该文件会在 JVM 退出时,进行删除 |
|||
* |
|||
* @param data 文件内容 |
|||
* @return 文件 |
|||
*/ |
|||
@SneakyThrows |
|||
public static File createTempFile(String data) { |
|||
File file = createTempFile(); |
|||
// 写入内容
|
|||
FileUtil.writeUtf8String(data, file); |
|||
return file; |
|||
} |
|||
|
|||
/** |
|||
* 创建临时文件 |
|||
* 该文件会在 JVM 退出时,进行删除 |
|||
* |
|||
* @param data 文件内容 |
|||
* @return 文件 |
|||
*/ |
|||
@SneakyThrows |
|||
public static File createTempFile(byte[] data) { |
|||
File file = createTempFile(); |
|||
// 写入内容
|
|||
FileUtil.writeBytes(data, file); |
|||
return file; |
|||
} |
|||
|
|||
/** |
|||
* 创建临时文件,无内容 |
|||
* 该文件会在 JVM 退出时,进行删除 |
|||
* |
|||
* @return 文件 |
|||
*/ |
|||
@SneakyThrows |
|||
public static File createTempFile() { |
|||
// 创建文件,通过 UUID 保证唯一
|
|||
File file = File.createTempFile(IdUtil.simpleUUID(), null); |
|||
// 标记 JVM 退出时,自动删除
|
|||
file.deleteOnExit(); |
|||
return file; |
|||
} |
|||
|
|||
/** |
|||
* 生成文件路径 |
|||
* |
|||
* @param content 文件内容 |
|||
* @param originalName 原始文件名 |
|||
* @return path,唯一不可重复 |
|||
*/ |
|||
public static String generatePath(byte[] content, String originalName) { |
|||
String sha256Hex = DigestUtil.sha256Hex(content); |
|||
// 情况一:如果存在 name,则优先使用 name 的后缀
|
|||
if (StrUtil.isNotBlank(originalName)) { |
|||
String extName = FileNameUtil.extName(originalName); |
|||
return StrUtil.isBlank(extName) ? sha256Hex : sha256Hex + "." + extName; |
|||
} |
|||
// 情况二:基于 content 计算
|
|||
return sha256Hex + '.' + FileTypeUtil.getType(new ByteArrayInputStream(content)); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.win.framework.common.util.io; |
|||
|
|||
import cn.hutool.core.io.IORuntimeException; |
|||
import cn.hutool.core.io.IoUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
|
|||
import java.io.InputStream; |
|||
|
|||
/** |
|||
* IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class IoUtils { |
|||
|
|||
/** |
|||
* 从流中读取 UTF8 编码的内容 |
|||
* |
|||
* @param in 输入流 |
|||
* @param isClose 是否关闭 |
|||
* @return 内容 |
|||
* @throws IORuntimeException IO 异常 |
|||
*/ |
|||
public static String readUtf8(InputStream in, boolean isClose) throws IORuntimeException { |
|||
return StrUtil.utf8Str(IoUtil.read(in, isClose)); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,202 @@ |
|||
package com.win.framework.common.util.json; |
|||
|
|||
import cn.hutool.core.util.ArrayUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import cn.hutool.json.JSONUtil; |
|||
import com.fasterxml.jackson.annotation.JsonInclude; |
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import com.fasterxml.jackson.databind.DeserializationFeature; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import com.fasterxml.jackson.databind.SerializationFeature; |
|||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; |
|||
import lombok.SneakyThrows; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.io.IOException; |
|||
import java.lang.reflect.Type; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* JSON 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Slf4j |
|||
public class JsonUtils { |
|||
|
|||
private static ObjectMapper objectMapper = new ObjectMapper(); |
|||
|
|||
static { |
|||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); |
|||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
|||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
|
|||
objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
|
|||
} |
|||
|
|||
/** |
|||
* 初始化 objectMapper 属性 |
|||
* <p> |
|||
* 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean |
|||
* |
|||
* @param objectMapper ObjectMapper 对象 |
|||
*/ |
|||
public static void init(ObjectMapper objectMapper) { |
|||
JsonUtils.objectMapper = objectMapper; |
|||
} |
|||
|
|||
@SneakyThrows |
|||
public static String toJsonString(Object object) { |
|||
return objectMapper.writeValueAsString(object); |
|||
} |
|||
|
|||
@SneakyThrows |
|||
public static byte[] toJsonByte(Object object) { |
|||
return objectMapper.writeValueAsBytes(object); |
|||
} |
|||
|
|||
@SneakyThrows |
|||
public static String toJsonPrettyString(Object object) { |
|||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); |
|||
} |
|||
|
|||
public static <T> T parseObject(String text, Class<T> clazz) { |
|||
if (StrUtil.isEmpty(text)) { |
|||
return null; |
|||
} |
|||
try { |
|||
return objectMapper.readValue(text, clazz); |
|||
} catch (IOException e) { |
|||
log.error("json parse err,json:{}", text, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
public static <T> T parseObject(String text, String path, Class<T> clazz) { |
|||
if (StrUtil.isEmpty(text)) { |
|||
return null; |
|||
} |
|||
try { |
|||
JsonNode treeNode = objectMapper.readTree(text); |
|||
JsonNode pathNode = treeNode.path(path); |
|||
return objectMapper.readValue(pathNode.toString(), clazz); |
|||
} catch (IOException e) { |
|||
log.error("json parse err,json:{}", text, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
public static <T> T parseObject(String text, Type type) { |
|||
if (StrUtil.isEmpty(text)) { |
|||
return null; |
|||
} |
|||
try { |
|||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type)); |
|||
} catch (IOException e) { |
|||
log.error("json parse err,json:{}", text, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 将字符串解析成指定类型的对象 |
|||
* 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下, |
|||
* 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。 |
|||
* |
|||
* @param text 字符串 |
|||
* @param clazz 类型 |
|||
* @return 对象 |
|||
*/ |
|||
public static <T> T parseObject2(String text, Class<T> clazz) { |
|||
if (StrUtil.isEmpty(text)) { |
|||
return null; |
|||
} |
|||
return JSONUtil.toBean(text, clazz); |
|||
} |
|||
|
|||
public static <T> T parseObject(byte[] bytes, Class<T> clazz) { |
|||
if (ArrayUtil.isEmpty(bytes)) { |
|||
return null; |
|||
} |
|||
try { |
|||
return objectMapper.readValue(bytes, clazz); |
|||
} catch (IOException e) { |
|||
log.error("json parse err,json:{}", bytes, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
public static <T> T parseObject(String text, TypeReference<T> typeReference) { |
|||
try { |
|||
return objectMapper.readValue(text, typeReference); |
|||
} catch (IOException e) { |
|||
log.error("json parse err,json:{}", text, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null |
|||
* |
|||
* @param text 字符串 |
|||
* @param typeReference 类型引用 |
|||
* @return 指定类型的对象 |
|||
*/ |
|||
public static <T> T parseObjectQuietly(String text, TypeReference<T> typeReference) { |
|||
try { |
|||
return objectMapper.readValue(text, typeReference); |
|||
} catch (IOException e) { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public static <T> List<T> parseArray(String text, Class<T> clazz) { |
|||
if (StrUtil.isEmpty(text)) { |
|||
return new ArrayList<>(); |
|||
} |
|||
try { |
|||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); |
|||
} catch (IOException e) { |
|||
log.error("json parse err,json:{}", text, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
public static <T> List<T> parseArray(String text, String path, Class<T> clazz) { |
|||
if (StrUtil.isEmpty(text)) { |
|||
return null; |
|||
} |
|||
try { |
|||
JsonNode treeNode = objectMapper.readTree(text); |
|||
JsonNode pathNode = treeNode.path(path); |
|||
return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); |
|||
} catch (IOException e) { |
|||
log.error("json parse err,json:{}", text, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
public static JsonNode parseTree(String text) { |
|||
try { |
|||
return objectMapper.readTree(text); |
|||
} catch (IOException e) { |
|||
log.error("json parse err,json:{}", text, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
public static JsonNode parseTree(byte[] text) { |
|||
try { |
|||
return objectMapper.readTree(text); |
|||
} catch (IOException e) { |
|||
log.error("json parse err,json:{}", text, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
public static boolean isJson(String text) { |
|||
return JSONUtil.isTypeJSON(text); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,37 @@ |
|||
package com.win.framework.common.util.json.databind; |
|||
|
|||
import com.fasterxml.jackson.core.JsonGenerator; |
|||
import com.fasterxml.jackson.databind.SerializerProvider; |
|||
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; |
|||
|
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* Long 序列化规则 |
|||
* |
|||
* 会将超长 long 值转换为 string,解决前端 JavaScript 最大安全整数是 2^53-1 的问题 |
|||
* |
|||
* @author 星语 |
|||
*/ |
|||
@JacksonStdImpl |
|||
public class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.NumberSerializer { |
|||
|
|||
private static final long MAX_SAFE_INTEGER = 9007199254740991L; |
|||
private static final long MIN_SAFE_INTEGER = -9007199254740991L; |
|||
|
|||
public static final NumberSerializer INSTANCE = new NumberSerializer(Number.class); |
|||
|
|||
public NumberSerializer(Class<? extends Number> rawType) { |
|||
super(rawType); |
|||
} |
|||
|
|||
@Override |
|||
public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException { |
|||
// 超出范围 序列化位字符串
|
|||
if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { |
|||
super.serialize(value, gen, serializers); |
|||
} else { |
|||
gen.writeString(value.toString()); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,27 @@ |
|||
package com.win.framework.common.util.json.databind; |
|||
|
|||
import com.fasterxml.jackson.core.JsonParser; |
|||
import com.fasterxml.jackson.databind.DeserializationContext; |
|||
import com.fasterxml.jackson.databind.JsonDeserializer; |
|||
|
|||
import java.io.IOException; |
|||
import java.time.Instant; |
|||
import java.time.LocalDateTime; |
|||
import java.time.ZoneId; |
|||
|
|||
/** |
|||
* 基于时间戳的 LocalDateTime 反序列化器 |
|||
* |
|||
* @author 老五 |
|||
*/ |
|||
public class TimestampLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> { |
|||
|
|||
public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer(); |
|||
|
|||
@Override |
|||
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { |
|||
// 将 Long 时间戳,转换为 LocalDateTime 对象
|
|||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault()); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.win.framework.common.util.json.databind; |
|||
|
|||
import com.fasterxml.jackson.core.JsonGenerator; |
|||
import com.fasterxml.jackson.databind.JsonSerializer; |
|||
import com.fasterxml.jackson.databind.SerializerProvider; |
|||
|
|||
import java.io.IOException; |
|||
import java.time.LocalDateTime; |
|||
import java.time.ZoneId; |
|||
|
|||
/** |
|||
* 基于时间戳的 LocalDateTime 序列化器 |
|||
* |
|||
* @author 老五 |
|||
*/ |
|||
public class TimestampLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { |
|||
|
|||
public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer(); |
|||
|
|||
@Override |
|||
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { |
|||
// 将 LocalDateTime 对象,转换为 Long 时间戳
|
|||
gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,30 @@ |
|||
package com.win.framework.common.util.monitor; |
|||
|
|||
import org.apache.skywalking.apm.toolkit.trace.TraceContext; |
|||
|
|||
/** |
|||
* 链路追踪工具类 |
|||
* |
|||
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class TracerUtils { |
|||
|
|||
/** |
|||
* 私有化构造方法 |
|||
*/ |
|||
private TracerUtils() { |
|||
} |
|||
|
|||
/** |
|||
* 获得链路追踪编号,直接返回 SkyWalking 的 TraceId。 |
|||
* 如果不存在的话为空字符串!!! |
|||
* |
|||
* @return 链路追踪编号 |
|||
*/ |
|||
public static String getTraceId() { |
|||
return TraceContext.traceId(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,131 @@ |
|||
package com.win.framework.common.util.number; |
|||
|
|||
import cn.hutool.core.math.Money; |
|||
import cn.hutool.core.util.NumberUtil; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.math.RoundingMode; |
|||
|
|||
/** |
|||
* 金额工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class MoneyUtils { |
|||
|
|||
/** |
|||
* 金额的小数位数 |
|||
*/ |
|||
private static final int PRICE_SCALE = 2; |
|||
|
|||
/** |
|||
* 百分比对应的 BigDecimal 对象 |
|||
*/ |
|||
public static final BigDecimal PERCENT_100 = BigDecimal.valueOf(100); |
|||
|
|||
/** |
|||
* 计算百分比金额,四舍五入 |
|||
* |
|||
* @param price 金额 |
|||
* @param rate 百分比,例如说 56.77% 则传入 56.77 |
|||
* @return 百分比金额 |
|||
*/ |
|||
public static Integer calculateRatePrice(Integer price, Double rate) { |
|||
return calculateRatePrice(price, rate, 0, RoundingMode.HALF_UP).intValue(); |
|||
} |
|||
|
|||
/** |
|||
* 计算百分比金额,向下传入 |
|||
* |
|||
* @param price 金额 |
|||
* @param rate 百分比,例如说 56.77% 则传入 56.77 |
|||
* @return 百分比金额 |
|||
*/ |
|||
public static Integer calculateRatePriceFloor(Integer price, Double rate) { |
|||
return calculateRatePrice(price, rate, 0, RoundingMode.FLOOR).intValue(); |
|||
} |
|||
|
|||
/** |
|||
* 计算百分比金额 |
|||
* |
|||
* @param price 金额(单位分) |
|||
* @param count 数量 |
|||
* @param percent 折扣(单位分),列如 60.2%,则传入 6020 |
|||
* @return 商品总价 |
|||
*/ |
|||
public static Integer calculator(Integer price, Integer count, Integer percent) { |
|||
price = price * count; |
|||
if (percent == null) { |
|||
return price; |
|||
} |
|||
return MoneyUtils.calculateRatePriceFloor(price, (double) (percent / 100)); |
|||
} |
|||
|
|||
/** |
|||
* 计算百分比金额 |
|||
* |
|||
* @param price 金额 |
|||
* @param rate 百分比,例如说 56.77% 则传入 56.77 |
|||
* @param scale 保留小数位数 |
|||
* @param roundingMode 舍入模式 |
|||
*/ |
|||
public static BigDecimal calculateRatePrice(Number price, Number rate, int scale, RoundingMode roundingMode) { |
|||
return NumberUtil.toBigDecimal(price).multiply(NumberUtil.toBigDecimal(rate)) // 乘以
|
|||
.divide(BigDecimal.valueOf(100), scale, roundingMode); // 除以 100
|
|||
} |
|||
|
|||
/** |
|||
* 分转元 |
|||
* |
|||
* @param fen 分 |
|||
* @return 元 |
|||
*/ |
|||
public static BigDecimal fenToYuan(int fen) { |
|||
return new Money(0, fen).getAmount(); |
|||
} |
|||
|
|||
/** |
|||
* 分转元(字符串) |
|||
* |
|||
* 例如说 fen 为 1 时,则结果为 0.01 |
|||
* |
|||
* @param fen 分 |
|||
* @return 元 |
|||
*/ |
|||
public static String fenToYuanStr(int fen) { |
|||
return new Money(0, fen).toString(); |
|||
} |
|||
|
|||
/** |
|||
* 金额相乘,默认进行四舍五入 |
|||
* |
|||
* 位数:{@link #PRICE_SCALE} |
|||
* |
|||
* @param price 金额 |
|||
* @param count 数量 |
|||
* @return 金额相乘结果 |
|||
*/ |
|||
public static BigDecimal priceMultiply(BigDecimal price, BigDecimal count) { |
|||
if (price == null || count == null) { |
|||
return null; |
|||
} |
|||
return price.multiply(count).setScale(PRICE_SCALE, RoundingMode.HALF_UP); |
|||
} |
|||
|
|||
/** |
|||
* 金额相乘(百分比),默认进行四舍五入 |
|||
* |
|||
* 位数:{@link #PRICE_SCALE} |
|||
* |
|||
* @param price 金额 |
|||
* @param percent 百分比 |
|||
* @return 金额相乘结果 |
|||
*/ |
|||
public static BigDecimal priceMultiplyPercent(BigDecimal price, BigDecimal percent) { |
|||
if (price == null || percent == null) { |
|||
return null; |
|||
} |
|||
return price.multiply(percent).divide(PERCENT_100, PRICE_SCALE, RoundingMode.HALF_UP); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,64 @@ |
|||
package com.win.framework.common.util.number; |
|||
|
|||
import cn.hutool.core.util.NumberUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
|
|||
import java.math.BigDecimal; |
|||
|
|||
/** |
|||
* 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class NumberUtils { |
|||
|
|||
public static Long parseLong(String str) { |
|||
return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null; |
|||
} |
|||
|
|||
public static Integer parseInt(String str) { |
|||
return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null; |
|||
} |
|||
|
|||
/** |
|||
* 通过经纬度获取地球上两点之间的距离 |
|||
* |
|||
* 参考 <<a href="https://gitee.com/dromara/hutool/blob/1caabb586b1f95aec66a21d039c5695df5e0f4c1/hutool-core/src/main/java/cn/hutool/core/util/DistanceUtil.java">DistanceUtil</a>> 实现,目前它已经被 hutool 删除 |
|||
* |
|||
* @param lat1 经度1 |
|||
* @param lng1 纬度1 |
|||
* @param lat2 经度2 |
|||
* @param lng2 纬度2 |
|||
* @return 距离,单位:千米 |
|||
*/ |
|||
public static double getDistance(double lat1, double lng1, double lat2, double lng2) { |
|||
double radLat1 = lat1 * Math.PI / 180.0; |
|||
double radLat2 = lat2 * Math.PI / 180.0; |
|||
double a = radLat1 - radLat2; |
|||
double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0; |
|||
double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) |
|||
+ Math.cos(radLat1) * Math.cos(radLat2) |
|||
* Math.pow(Math.sin(b / 2), 2))); |
|||
distance = distance * 6378.137; |
|||
distance = Math.round(distance * 10000d) / 10000d; |
|||
return distance; |
|||
} |
|||
|
|||
/** |
|||
* 提供精确的乘法运算 |
|||
* |
|||
* 和 hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是,如果存在 null,则返回 null |
|||
* |
|||
* @param values 多个被乘值 |
|||
* @return 积 |
|||
*/ |
|||
public static BigDecimal mul(BigDecimal... values) { |
|||
for (BigDecimal value : values) { |
|||
if (value == null) { |
|||
return null; |
|||
} |
|||
} |
|||
return NumberUtil.mul(values); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,69 @@ |
|||
package com.win.framework.common.util.object; |
|||
|
|||
import cn.hutool.core.bean.BeanUtil; |
|||
import com.win.framework.common.pojo.PageResult; |
|||
import com.win.framework.common.util.collection.CollectionUtils; |
|||
|
|||
import java.util.List; |
|||
import java.util.function.Consumer; |
|||
|
|||
/** |
|||
* Bean 工具类 |
|||
* |
|||
* 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能 |
|||
* 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class BeanUtils { |
|||
|
|||
public static <T> T toBean(Object source, Class<T> targetClass) { |
|||
return BeanUtil.toBean(source, targetClass); |
|||
} |
|||
|
|||
public static <T> T toBean(Object source, Class<T> targetClass, Consumer<T> peek) { |
|||
T target = toBean(source, targetClass); |
|||
if (target != null) { |
|||
peek.accept(target); |
|||
} |
|||
return target; |
|||
} |
|||
|
|||
public static <S, T> List<T> toBean(List<S> source, Class<T> targetType) { |
|||
if (source == null) { |
|||
return null; |
|||
} |
|||
return CollectionUtils.convertList(source, s -> toBean(s, targetType)); |
|||
} |
|||
|
|||
public static <S, T> List<T> toBean(List<S> source, Class<T> targetType, Consumer<T> peek) { |
|||
List<T> list = toBean(source, targetType); |
|||
if (list != null) { |
|||
list.forEach(peek); |
|||
} |
|||
return list; |
|||
} |
|||
|
|||
public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType) { |
|||
return toBean(source, targetType, null); |
|||
} |
|||
|
|||
public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType, Consumer<T> peek) { |
|||
if (source == null) { |
|||
return null; |
|||
} |
|||
List<T> list = toBean(source.getList(), targetType); |
|||
if (peek != null) { |
|||
list.forEach(peek); |
|||
} |
|||
return new PageResult<>(list, source.getTotal()); |
|||
} |
|||
|
|||
public static void copyProperties(Object source, Object target) { |
|||
if (source == null || target == null) { |
|||
return; |
|||
} |
|||
BeanUtil.copyProperties(source, target, false); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,63 @@ |
|||
package com.win.framework.common.util.object; |
|||
|
|||
import cn.hutool.core.util.ObjectUtil; |
|||
import cn.hutool.core.util.ReflectUtil; |
|||
|
|||
import java.lang.reflect.Field; |
|||
import java.util.Arrays; |
|||
import java.util.function.Consumer; |
|||
|
|||
/** |
|||
* Object 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class ObjectUtils { |
|||
|
|||
/** |
|||
* 复制对象,并忽略 Id 编号 |
|||
* |
|||
* @param object 被复制对象 |
|||
* @param consumer 消费者,可以二次编辑被复制对象 |
|||
* @return 复制后的对象 |
|||
*/ |
|||
public static <T> T cloneIgnoreId(T object, Consumer<T> consumer) { |
|||
T result = ObjectUtil.clone(object); |
|||
// 忽略 id 编号
|
|||
Field field = ReflectUtil.getField(object.getClass(), "id"); |
|||
if (field != null) { |
|||
ReflectUtil.setFieldValue(result, field, null); |
|||
} |
|||
// 二次编辑
|
|||
if (result != null) { |
|||
consumer.accept(result); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
public static <T extends Comparable<T>> T max(T obj1, T obj2) { |
|||
if (obj1 == null) { |
|||
return obj2; |
|||
} |
|||
if (obj2 == null) { |
|||
return obj1; |
|||
} |
|||
return obj1.compareTo(obj2) > 0 ? obj1 : obj2; |
|||
} |
|||
|
|||
@SafeVarargs |
|||
public static <T> T defaultIfNull(T... array) { |
|||
for (T item : array) { |
|||
if (item != null) { |
|||
return item; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
@SafeVarargs |
|||
public static <T> boolean equalsAny(T obj, T... array) { |
|||
return Arrays.asList(array).contains(obj); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,67 @@ |
|||
package com.win.framework.common.util.object; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.lang.func.Func1; |
|||
import cn.hutool.core.lang.func.LambdaUtil; |
|||
import cn.hutool.core.util.ArrayUtil; |
|||
import com.win.framework.common.pojo.PageParam; |
|||
import com.win.framework.common.pojo.SortablePageParam; |
|||
import com.win.framework.common.pojo.SortingField; |
|||
import org.springframework.util.Assert; |
|||
|
|||
import static java.util.Collections.singletonList; |
|||
|
|||
/** |
|||
* {@link com.win.framework.common.pojo.PageParam} 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class PageUtils { |
|||
|
|||
private static final Object[] ORDER_TYPES = new String[]{SortingField.ORDER_ASC, SortingField.ORDER_DESC}; |
|||
|
|||
public static int getStart(PageParam pageParam) { |
|||
return (pageParam.getPageNo() - 1) * pageParam.getPageSize(); |
|||
} |
|||
|
|||
/** |
|||
* 构建排序字段(默认倒序) |
|||
* |
|||
* @param func 排序字段的 Lambda 表达式 |
|||
* @param <T> 排序字段所属的类型 |
|||
* @return 排序字段 |
|||
*/ |
|||
public static <T> SortingField buildSortingField(Func1<T, ?> func) { |
|||
return buildSortingField(func, SortingField.ORDER_DESC); |
|||
} |
|||
|
|||
/** |
|||
* 构建排序字段 |
|||
* |
|||
* @param func 排序字段的 Lambda 表达式 |
|||
* @param order 排序类型 {@link SortingField#ORDER_ASC} {@link SortingField#ORDER_DESC} |
|||
* @param <T> 排序字段所属的类型 |
|||
* @return 排序字段 |
|||
*/ |
|||
public static <T> SortingField buildSortingField(Func1<T, ?> func, String order) { |
|||
Assert.isTrue(ArrayUtil.contains(ORDER_TYPES, order), String.format("字段的排序类型只能是 %s/%s", ORDER_TYPES)); |
|||
|
|||
String fieldName = LambdaUtil.getFieldName(func); |
|||
return new SortingField(fieldName, order); |
|||
} |
|||
|
|||
/** |
|||
* 构建默认的排序字段 |
|||
* 如果排序字段为空,则设置排序字段;否则忽略 |
|||
* |
|||
* @param sortablePageParam 排序分页查询参数 |
|||
* @param func 排序字段的 Lambda 表达式 |
|||
* @param <T> 排序字段所属的类型 |
|||
*/ |
|||
public static <T> void buildDefaultSortingField(SortablePageParam sortablePageParam, Func1<T, ?> func) { |
|||
if (sortablePageParam != null && CollUtil.isEmpty(sortablePageParam.getSortingFields())) { |
|||
sortablePageParam.setSortingFields(singletonList(buildSortingField(func))); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,7 @@ |
|||
/** |
|||
* 对于工具类的选择,优先查找 Hutool 中有没对应的方法 |
|||
* 如果没有,则自己封装对应的工具类,以 Utils 结尾,用于区分 |
|||
* |
|||
* ps:如果担心 Hutool 存在坑的问题,可以阅读 Hutool 的实现源码,以确保可靠性。并且,可以补充相关的单元测试。 |
|||
*/ |
|||
package com.win.framework.common.util; |
@ -0,0 +1,101 @@ |
|||
package com.win.framework.common.util.servlet; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import cn.hutool.extra.servlet.JakartaServletUtil; |
|||
import com.win.framework.common.util.json.JsonUtils; |
|||
import jakarta.servlet.ServletRequest; |
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import jakarta.servlet.http.HttpServletResponse; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.web.context.request.RequestAttributes; |
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import org.springframework.web.context.request.ServletRequestAttributes; |
|||
|
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 客户端工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class ServletUtils { |
|||
|
|||
/** |
|||
* 返回 JSON 字符串 |
|||
* |
|||
* @param response 响应 |
|||
* @param object 对象,会序列化成 JSON 字符串 |
|||
*/ |
|||
@SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
|
|||
public static void writeJSON(HttpServletResponse response, Object object) { |
|||
String content = JsonUtils.toJsonString(object); |
|||
JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE); |
|||
} |
|||
|
|||
/** |
|||
* @param request 请求 |
|||
* @return ua |
|||
*/ |
|||
public static String getUserAgent(HttpServletRequest request) { |
|||
String ua = request.getHeader("User-Agent"); |
|||
return ua != null ? ua : ""; |
|||
} |
|||
|
|||
/** |
|||
* 获得请求 |
|||
* |
|||
* @return HttpServletRequest |
|||
*/ |
|||
public static HttpServletRequest getRequest() { |
|||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); |
|||
if (!(requestAttributes instanceof ServletRequestAttributes)) { |
|||
return null; |
|||
} |
|||
return ((ServletRequestAttributes) requestAttributes).getRequest(); |
|||
} |
|||
|
|||
public static String getUserAgent() { |
|||
HttpServletRequest request = getRequest(); |
|||
if (request == null) { |
|||
return null; |
|||
} |
|||
return getUserAgent(request); |
|||
} |
|||
|
|||
public static String getClientIP() { |
|||
HttpServletRequest request = getRequest(); |
|||
if (request == null) { |
|||
return null; |
|||
} |
|||
return JakartaServletUtil.getClientIP(request); |
|||
} |
|||
|
|||
public static boolean isJsonRequest(ServletRequest request) { |
|||
return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE); |
|||
} |
|||
|
|||
public static String getBody(HttpServletRequest request) { |
|||
// 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取
|
|||
if (isJsonRequest(request)) { |
|||
return JakartaServletUtil.getBody(request); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static byte[] getBodyBytes(HttpServletRequest request) { |
|||
// 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取
|
|||
if (isJsonRequest(request)) { |
|||
return JakartaServletUtil.getBodyBytes(request); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static String getClientIP(HttpServletRequest request) { |
|||
return JakartaServletUtil.getClientIP(request); |
|||
} |
|||
|
|||
public static Map<String, String> getParamMap(HttpServletRequest request) { |
|||
return JakartaServletUtil.getParamMap(request); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,109 @@ |
|||
package com.win.framework.common.util.spring; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.map.MapUtil; |
|||
import cn.hutool.core.util.ArrayUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import cn.hutool.extra.spring.SpringUtil; |
|||
import org.aspectj.lang.JoinPoint; |
|||
import org.aspectj.lang.reflect.MethodSignature; |
|||
import org.springframework.context.expression.BeanFactoryResolver; |
|||
import org.springframework.core.DefaultParameterNameDiscoverer; |
|||
import org.springframework.core.ParameterNameDiscoverer; |
|||
import org.springframework.expression.EvaluationContext; |
|||
import org.springframework.expression.Expression; |
|||
import org.springframework.expression.ExpressionParser; |
|||
import org.springframework.expression.spel.standard.SpelExpressionParser; |
|||
import org.springframework.expression.spel.support.StandardEvaluationContext; |
|||
|
|||
import java.lang.reflect.Method; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* Spring EL 表达式的工具类 |
|||
* |
|||
* @author mashu |
|||
*/ |
|||
public class SpringExpressionUtils { |
|||
|
|||
/** |
|||
* Spring EL 表达式解析器 |
|||
*/ |
|||
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); |
|||
/** |
|||
* 参数名发现器 |
|||
*/ |
|||
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); |
|||
|
|||
private SpringExpressionUtils() { |
|||
} |
|||
|
|||
/** |
|||
* 从切面中,单个解析 EL 表达式的结果 |
|||
* |
|||
* @param joinPoint 切面点 |
|||
* @param expressionString EL 表达式数组 |
|||
* @return 执行界面 |
|||
*/ |
|||
public static Object parseExpression(JoinPoint joinPoint, String expressionString) { |
|||
Map<String, Object> result = parseExpressions(joinPoint, Collections.singletonList(expressionString)); |
|||
return result.get(expressionString); |
|||
} |
|||
|
|||
/** |
|||
* 从切面中,批量解析 EL 表达式的结果 |
|||
* |
|||
* @param joinPoint 切面点 |
|||
* @param expressionStrings EL 表达式数组 |
|||
* @return 结果,key 为表达式,value 为对应值 |
|||
*/ |
|||
public static Map<String, Object> parseExpressions(JoinPoint joinPoint, List<String> expressionStrings) { |
|||
// 如果为空,则不进行解析
|
|||
if (CollUtil.isEmpty(expressionStrings)) { |
|||
return MapUtil.newHashMap(); |
|||
} |
|||
|
|||
// 第一步,构建解析的上下文 EvaluationContext
|
|||
// 通过 joinPoint 获取被注解方法
|
|||
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); |
|||
Method method = methodSignature.getMethod(); |
|||
// 使用 spring 的 ParameterNameDiscoverer 获取方法形参名数组
|
|||
String[] paramNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method); |
|||
// Spring 的表达式上下文对象
|
|||
EvaluationContext context = new StandardEvaluationContext(); |
|||
// 给上下文赋值
|
|||
if (ArrayUtil.isNotEmpty(paramNames)) { |
|||
Object[] args = joinPoint.getArgs(); |
|||
for (int i = 0; i < paramNames.length; i++) { |
|||
context.setVariable(paramNames[i], args[i]); |
|||
} |
|||
} |
|||
|
|||
// 第二步,逐个参数解析
|
|||
Map<String, Object> result = MapUtil.newHashMap(expressionStrings.size(), true); |
|||
expressionStrings.forEach(key -> { |
|||
Object value = EXPRESSION_PARSER.parseExpression(key).getValue(context); |
|||
result.put(key, value); |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 从 Bean 工厂,解析 EL 表达式的结果 |
|||
* |
|||
* @param expressionString EL 表达式 |
|||
* @return 执行界面 |
|||
*/ |
|||
public static Object parseExpression(String expressionString) { |
|||
if (StrUtil.isBlank(expressionString)) { |
|||
return null; |
|||
} |
|||
Expression expression = EXPRESSION_PARSER.parseExpression(expressionString); |
|||
StandardEvaluationContext context = new StandardEvaluationContext(); |
|||
context.setBeanResolver(new BeanFactoryResolver(SpringUtil.getApplicationContext())); |
|||
return expression.getValue(context); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.win.framework.common.util.spring; |
|||
|
|||
import cn.hutool.extra.spring.SpringUtil; |
|||
|
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* Spring 工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class SpringUtils extends SpringUtil { |
|||
|
|||
/** |
|||
* 是否为生产环境 |
|||
* |
|||
* @return 是否生产环境 |
|||
*/ |
|||
public static boolean isProd() { |
|||
String activeProfile = getActiveProfile(); |
|||
return Objects.equals("prod", activeProfile); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,80 @@ |
|||
package com.win.framework.common.util.string; |
|||
|
|||
import cn.hutool.core.text.StrPool; |
|||
import cn.hutool.core.util.ArrayUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* 字符串工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class StrUtils { |
|||
|
|||
public static String maxLength(CharSequence str, int maxLength) { |
|||
return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
|
|||
} |
|||
|
|||
/** |
|||
* 给定字符串是否以任何一个字符串开始 |
|||
* 给定字符串和数组为空都返回 false |
|||
* |
|||
* @param str 给定字符串 |
|||
* @param prefixes 需要检测的开始字符串 |
|||
* @since 3.0.6 |
|||
*/ |
|||
public static boolean startWithAny(String str, Collection<String> prefixes) { |
|||
if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) { |
|||
return false; |
|||
} |
|||
|
|||
for (CharSequence suffix : prefixes) { |
|||
if (StrUtil.startWith(str, suffix, false)) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public static List<Long> splitToLong(String value, CharSequence separator) { |
|||
long[] longs = StrUtil.splitToLong(value, separator); |
|||
return Arrays.stream(longs).boxed().collect(Collectors.toList()); |
|||
} |
|||
|
|||
public static Set<Long> splitToLongSet(String value) { |
|||
return splitToLongSet(value, StrPool.COMMA); |
|||
} |
|||
|
|||
public static Set<Long> splitToLongSet(String value, CharSequence separator) { |
|||
long[] longs = StrUtil.splitToLong(value, separator); |
|||
return Arrays.stream(longs).boxed().collect(Collectors.toSet()); |
|||
} |
|||
|
|||
public static List<Integer> splitToInteger(String value, CharSequence separator) { |
|||
int[] integers = StrUtil.splitToInt(value, separator); |
|||
return Arrays.stream(integers).boxed().collect(Collectors.toList()); |
|||
} |
|||
|
|||
/** |
|||
* 移除字符串中,包含指定字符串的行 |
|||
* |
|||
* @param content 字符串 |
|||
* @param sequence 包含的字符串 |
|||
* @return 移除后的字符串 |
|||
*/ |
|||
public static String removeLineContains(String content, String sequence) { |
|||
if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) { |
|||
return content; |
|||
} |
|||
return Arrays.stream(content.split("\n")) |
|||
.filter(line -> !line.contains(sequence)) |
|||
.collect(Collectors.joining("\n")); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,55 @@ |
|||
package com.win.framework.common.util.validation; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.lang.Assert; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import jakarta.validation.ConstraintViolation; |
|||
import jakarta.validation.ConstraintViolationException; |
|||
import jakarta.validation.Validation; |
|||
import jakarta.validation.Validator; |
|||
import java.util.Set; |
|||
import java.util.regex.Pattern; |
|||
|
|||
/** |
|||
* 校验工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class ValidationUtils { |
|||
|
|||
private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$"); |
|||
|
|||
private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); |
|||
|
|||
private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*"); |
|||
|
|||
public static boolean isMobile(String mobile) { |
|||
return StringUtils.hasText(mobile) |
|||
&& PATTERN_MOBILE.matcher(mobile).matches(); |
|||
} |
|||
|
|||
public static boolean isURL(String url) { |
|||
return StringUtils.hasText(url) |
|||
&& PATTERN_URL.matcher(url).matches(); |
|||
} |
|||
|
|||
public static boolean isXmlNCName(String str) { |
|||
return StringUtils.hasText(str) |
|||
&& PATTERN_XML_NCNAME.matcher(str).matches(); |
|||
} |
|||
|
|||
public static void validate(Object object, Class<?>... groups) { |
|||
Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); |
|||
Assert.notNull(validator); |
|||
validate(validator, object, groups); |
|||
} |
|||
|
|||
public static void validate(Validator validator, Object object, Class<?>... groups) { |
|||
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups); |
|||
if (CollUtil.isNotEmpty(constraintViolations)) { |
|||
throw new ConstraintViolationException(constraintViolations); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,35 @@ |
|||
package com.win.framework.common.validation; |
|||
|
|||
import com.win.framework.common.core.IntArrayValuable; |
|||
|
|||
import jakarta.validation.Constraint; |
|||
import jakarta.validation.Payload; |
|||
import java.lang.annotation.*; |
|||
|
|||
@Target({ |
|||
ElementType.METHOD, |
|||
ElementType.FIELD, |
|||
ElementType.ANNOTATION_TYPE, |
|||
ElementType.CONSTRUCTOR, |
|||
ElementType.PARAMETER, |
|||
ElementType.TYPE_USE |
|||
}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
@Constraint( |
|||
validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class} |
|||
) |
|||
public @interface InEnum { |
|||
|
|||
/** |
|||
* @return 实现 EnumValuable 接口的 |
|||
*/ |
|||
Class<? extends IntArrayValuable> value(); |
|||
|
|||
String message() default "必须在指定范围 {value}"; |
|||
|
|||
Class<?>[] groups() default {}; |
|||
|
|||
Class<? extends Payload>[] payload() default {}; |
|||
|
|||
} |
@ -0,0 +1,42 @@ |
|||
package com.win.framework.common.validation; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import com.win.framework.common.core.IntArrayValuable; |
|||
|
|||
import jakarta.validation.ConstraintValidator; |
|||
import jakarta.validation.ConstraintValidatorContext; |
|||
import java.util.Arrays; |
|||
import java.util.Collection; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> { |
|||
|
|||
private List<Integer> values; |
|||
|
|||
@Override |
|||
public void initialize(InEnum annotation) { |
|||
IntArrayValuable[] values = annotation.value().getEnumConstants(); |
|||
if (values.length == 0) { |
|||
this.values = Collections.emptyList(); |
|||
} else { |
|||
this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public boolean isValid(Collection<Integer> list, ConstraintValidatorContext context) { |
|||
// 校验通过
|
|||
if (CollUtil.containsAll(values, list)) { |
|||
return true; |
|||
} |
|||
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
|
|||
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
|
|||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate() |
|||
.replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
|
|||
return false; |
|||
} |
|||
|
|||
} |
|||
|
@ -0,0 +1,44 @@ |
|||
package com.win.framework.common.validation; |
|||
|
|||
import com.win.framework.common.core.IntArrayValuable; |
|||
|
|||
import jakarta.validation.ConstraintValidator; |
|||
import jakarta.validation.ConstraintValidatorContext; |
|||
import java.util.Arrays; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
public class InEnumValidator implements ConstraintValidator<InEnum, Integer> { |
|||
|
|||
private List<Integer> values; |
|||
|
|||
@Override |
|||
public void initialize(InEnum annotation) { |
|||
IntArrayValuable[] values = annotation.value().getEnumConstants(); |
|||
if (values.length == 0) { |
|||
this.values = Collections.emptyList(); |
|||
} else { |
|||
this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public boolean isValid(Integer value, ConstraintValidatorContext context) { |
|||
// 为空时,默认不校验,即认为通过
|
|||
if (value == null) { |
|||
return true; |
|||
} |
|||
// 校验通过
|
|||
if (values.contains(value)) { |
|||
return true; |
|||
} |
|||
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
|
|||
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
|
|||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate() |
|||
.replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
|
|||
return false; |
|||
} |
|||
|
|||
} |
|||
|
@ -0,0 +1,28 @@ |
|||
package com.win.framework.common.validation; |
|||
|
|||
import jakarta.validation.Constraint; |
|||
import jakarta.validation.Payload; |
|||
import java.lang.annotation.*; |
|||
|
|||
@Target({ |
|||
ElementType.METHOD, |
|||
ElementType.FIELD, |
|||
ElementType.ANNOTATION_TYPE, |
|||
ElementType.CONSTRUCTOR, |
|||
ElementType.PARAMETER, |
|||
ElementType.TYPE_USE |
|||
}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
@Constraint( |
|||
validatedBy = MobileValidator.class |
|||
) |
|||
public @interface Mobile { |
|||
|
|||
String message() default "手机号格式不正确"; |
|||
|
|||
Class<?>[] groups() default {}; |
|||
|
|||
Class<? extends Payload>[] payload() default {}; |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.win.framework.common.validation; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.win.framework.common.util.validation.ValidationUtils; |
|||
|
|||
import jakarta.validation.ConstraintValidator; |
|||
import jakarta.validation.ConstraintValidatorContext; |
|||
|
|||
public class MobileValidator implements ConstraintValidator<Mobile, String> { |
|||
|
|||
@Override |
|||
public void initialize(Mobile annotation) { |
|||
} |
|||
|
|||
@Override |
|||
public boolean isValid(String value, ConstraintValidatorContext context) { |
|||
// 如果手机号为空,默认不校验,即校验通过
|
|||
if (StrUtil.isEmpty(value)) { |
|||
return true; |
|||
} |
|||
// 校验手机
|
|||
return ValidationUtils.isMobile(value); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.win.framework.common.validation; |
|||
|
|||
import jakarta.validation.Constraint; |
|||
import jakarta.validation.Payload; |
|||
import java.lang.annotation.*; |
|||
|
|||
@Target({ |
|||
ElementType.METHOD, |
|||
ElementType.FIELD, |
|||
ElementType.ANNOTATION_TYPE, |
|||
ElementType.CONSTRUCTOR, |
|||
ElementType.PARAMETER, |
|||
ElementType.TYPE_USE |
|||
}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
@Constraint( |
|||
validatedBy = TelephoneValidator.class |
|||
) |
|||
public @interface Telephone { |
|||
|
|||
String message() default "电话格式不正确"; |
|||
|
|||
Class<?>[] groups() default {}; |
|||
|
|||
Class<? extends Payload>[] payload() default {}; |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.win.framework.common.validation; |
|||
|
|||
import cn.hutool.core.text.CharSequenceUtil; |
|||
import cn.hutool.core.util.PhoneUtil; |
|||
|
|||
import jakarta.validation.ConstraintValidator; |
|||
import jakarta.validation.ConstraintValidatorContext; |
|||
|
|||
public class TelephoneValidator implements ConstraintValidator<Telephone, String> { |
|||
|
|||
@Override |
|||
public void initialize(Telephone annotation) { |
|||
} |
|||
|
|||
@Override |
|||
public boolean isValid(String value, ConstraintValidatorContext context) { |
|||
// 如果手机号为空,默认不校验,即校验通过
|
|||
if (CharSequenceUtil.isEmpty(value)) { |
|||
return true; |
|||
} |
|||
// 校验手机
|
|||
return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,4 @@ |
|||
/** |
|||
* 使用 Hibernate Validator 实现参数校验 |
|||
*/ |
|||
package com.win.framework.common.validation; |
@ -0,0 +1 @@ |
|||
<http://www.iocoder.cn/Spring-Boot/Validation/?win> |
@ -0,0 +1,46 @@ |
|||
<?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>win-framework</artifactId> |
|||
<groupId>com.win</groupId> |
|||
<version>${revision}</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<artifactId>win-spring-boot-starter-biz-data-permission</artifactId> |
|||
<packaging>jar</packaging> |
|||
|
|||
<name>${project.artifactId}</name> |
|||
<description>数据权限</description> |
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-common</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- Web 相关 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-security</artifactId> |
|||
<optional>true</optional> <!-- 可选,如果使用 DeptDataPermissionRule 必须提供 --> |
|||
</dependency> |
|||
|
|||
<!-- DB 相关 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-spring-boot-starter-mybatis</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- 业务组件 --> |
|||
<dependency> |
|||
<groupId>com.win</groupId> |
|||
<artifactId>win-module-system-api</artifactId> <!-- 需要使用它,进行数据权限的获取 --> |
|||
<version>${revision}</version> |
|||
</dependency> |
|||
|
|||
</dependencies> |
|||
|
|||
</project> |
@ -0,0 +1,46 @@ |
|||
package com.win.framework.datapermission.config; |
|||
|
|||
import com.win.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor; |
|||
import com.win.framework.datapermission.core.db.DataPermissionRuleHandler; |
|||
import com.win.framework.datapermission.core.rule.DataPermissionRule; |
|||
import com.win.framework.datapermission.core.rule.DataPermissionRuleFactory; |
|||
import com.win.framework.datapermission.core.rule.DataPermissionRuleFactoryImpl; |
|||
import com.win.framework.mybatis.core.util.MyBatisUtils; |
|||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
|||
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor; |
|||
import org.springframework.boot.autoconfigure.AutoConfiguration; |
|||
import org.springframework.context.annotation.Bean; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 数据权限的自动配置类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@AutoConfiguration |
|||
public class WinDataPermissionAutoConfiguration { |
|||
|
|||
@Bean |
|||
public DataPermissionRuleFactory dataPermissionRuleFactory(List<DataPermissionRule> rules) { |
|||
return new DataPermissionRuleFactoryImpl(rules); |
|||
} |
|||
|
|||
@Bean |
|||
public DataPermissionRuleHandler dataPermissionRuleHandler(MybatisPlusInterceptor interceptor, |
|||
DataPermissionRuleFactory ruleFactory) { |
|||
// 创建 DataPermissionInterceptor 拦截器
|
|||
DataPermissionRuleHandler handler = new DataPermissionRuleHandler(ruleFactory); |
|||
DataPermissionInterceptor inner = new DataPermissionInterceptor(handler); |
|||
// 添加到 interceptor 中
|
|||
// 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定
|
|||
MyBatisUtils.addInterceptor(interceptor, inner, 0); |
|||
return handler; |
|||
} |
|||
|
|||
@Bean |
|||
public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() { |
|||
return new DataPermissionAnnotationAdvisor(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,34 @@ |
|||
package com.win.framework.datapermission.config; |
|||
|
|||
import com.win.framework.datapermission.core.rule.dept.DeptDataPermissionRule; |
|||
import com.win.framework.datapermission.core.rule.dept.DeptDataPermissionRuleCustomizer; |
|||
import com.win.framework.security.core.LoginUser; |
|||
import com.win.module.system.api.permission.PermissionApi; |
|||
import org.springframework.boot.autoconfigure.AutoConfiguration; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
|||
import org.springframework.context.annotation.Bean; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 基于部门的数据权限 AutoConfiguration |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@AutoConfiguration |
|||
@ConditionalOnClass(LoginUser.class) |
|||
@ConditionalOnBean(value = {PermissionApi.class, DeptDataPermissionRuleCustomizer.class}) |
|||
public class WinDeptDataPermissionAutoConfiguration { |
|||
|
|||
@Bean |
|||
public DeptDataPermissionRule deptDataPermissionRule(PermissionApi permissionApi, |
|||
List<DeptDataPermissionRuleCustomizer> customizers) { |
|||
// 创建 DeptDataPermissionRule 对象
|
|||
DeptDataPermissionRule rule = new DeptDataPermissionRule(permissionApi); |
|||
// 补全表配置
|
|||
customizers.forEach(customizer -> customizer.customize(rule)); |
|||
return rule; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,35 @@ |
|||
package com.win.framework.datapermission.core.annotation; |
|||
|
|||
import com.win.framework.datapermission.core.rule.DataPermissionRule; |
|||
|
|||
import java.lang.annotation.*; |
|||
|
|||
/** |
|||
* 数据权限注解 |
|||
* 可声明在类或者方法上,标识使用的数据权限规则 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Target({ElementType.TYPE, ElementType.METHOD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
public @interface DataPermission { |
|||
|
|||
/** |
|||
* 当前类或方法是否开启数据权限 |
|||
* 即使不添加 @DataPermission 注解,默认是开启状态 |
|||
* 可通过设置 enable 为 false 禁用 |
|||
*/ |
|||
boolean enable() default true; |
|||
|
|||
/** |
|||
* 生效的数据权限规则数组,优先级高于 {@link #excludeRules()} |
|||
*/ |
|||
Class<? extends DataPermissionRule>[] includeRules() default {}; |
|||
|
|||
/** |
|||
* 排除的数据权限规则数组,优先级最低 |
|||
*/ |
|||
Class<? extends DataPermissionRule>[] excludeRules() default {}; |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.win.framework.datapermission.core.aop; |
|||
|
|||
import com.win.framework.datapermission.core.annotation.DataPermission; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.Getter; |
|||
import org.aopalliance.aop.Advice; |
|||
import org.springframework.aop.Pointcut; |
|||
import org.springframework.aop.support.AbstractPointcutAdvisor; |
|||
import org.springframework.aop.support.ComposablePointcut; |
|||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; |
|||
|
|||
/** |
|||
* {@link com.win.framework.datapermission.core.annotation.DataPermission} 注解的 Advisor 实现类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Getter |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class DataPermissionAnnotationAdvisor extends AbstractPointcutAdvisor { |
|||
|
|||
private final Advice advice; |
|||
|
|||
private final Pointcut pointcut; |
|||
|
|||
public DataPermissionAnnotationAdvisor() { |
|||
this.advice = new DataPermissionAnnotationInterceptor(); |
|||
this.pointcut = this.buildPointcut(); |
|||
} |
|||
|
|||
protected Pointcut buildPointcut() { |
|||
Pointcut classPointcut = new AnnotationMatchingPointcut(DataPermission.class, true); |
|||
Pointcut methodPointcut = new AnnotationMatchingPointcut(null, DataPermission.class, true); |
|||
return new ComposablePointcut(classPointcut).union(methodPointcut); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,72 @@ |
|||
package com.win.framework.datapermission.core.aop; |
|||
|
|||
import com.win.framework.datapermission.core.annotation.DataPermission; |
|||
import lombok.Getter; |
|||
import org.aopalliance.intercept.MethodInterceptor; |
|||
import org.aopalliance.intercept.MethodInvocation; |
|||
import org.springframework.core.MethodClassKey; |
|||
import org.springframework.core.annotation.AnnotationUtils; |
|||
|
|||
import java.lang.reflect.Method; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
/** |
|||
* {@link DataPermission} 注解的拦截器 |
|||
* 1. 在执行方法前,将 @DataPermission 注解入栈 |
|||
* 2. 在执行方法后,将 @DataPermission 注解出栈 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@DataPermission // 该注解,用于 {@link DATA_PERMISSION_NULL} 的空对象
|
|||
public class DataPermissionAnnotationInterceptor implements MethodInterceptor { |
|||
|
|||
/** |
|||
* DataPermission 空对象,用于方法无 {@link DataPermission} 注解时,使用 DATA_PERMISSION_NULL 进行占位 |
|||
*/ |
|||
static final DataPermission DATA_PERMISSION_NULL = DataPermissionAnnotationInterceptor.class.getAnnotation(DataPermission.class); |
|||
|
|||
@Getter |
|||
private final Map<MethodClassKey, DataPermission> dataPermissionCache = new ConcurrentHashMap<>(); |
|||
|
|||
@Override |
|||
public Object invoke(MethodInvocation methodInvocation) throws Throwable { |
|||
// 入栈
|
|||
DataPermission dataPermission = this.findAnnotation(methodInvocation); |
|||
if (dataPermission != null) { |
|||
DataPermissionContextHolder.add(dataPermission); |
|||
} |
|||
try { |
|||
// 执行逻辑
|
|||
return methodInvocation.proceed(); |
|||
} finally { |
|||
// 出栈
|
|||
if (dataPermission != null) { |
|||
DataPermissionContextHolder.remove(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private DataPermission findAnnotation(MethodInvocation methodInvocation) { |
|||
// 1. 从缓存中获取
|
|||
Method method = methodInvocation.getMethod(); |
|||
Object targetObject = methodInvocation.getThis(); |
|||
Class<?> clazz = targetObject != null ? targetObject.getClass() : method.getDeclaringClass(); |
|||
MethodClassKey methodClassKey = new MethodClassKey(method, clazz); |
|||
DataPermission dataPermission = dataPermissionCache.get(methodClassKey); |
|||
if (dataPermission != null) { |
|||
return dataPermission != DATA_PERMISSION_NULL ? dataPermission : null; |
|||
} |
|||
|
|||
// 2.1 从方法中获取
|
|||
dataPermission = AnnotationUtils.findAnnotation(method, DataPermission.class); |
|||
// 2.2 从类上获取
|
|||
if (dataPermission == null) { |
|||
dataPermission = AnnotationUtils.findAnnotation(clazz, DataPermission.class); |
|||
} |
|||
// 2.3 添加到缓存中
|
|||
dataPermissionCache.put(methodClassKey, dataPermission != null ? dataPermission : DATA_PERMISSION_NULL); |
|||
return dataPermission; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,72 @@ |
|||
package com.win.framework.datapermission.core.aop; |
|||
|
|||
import com.win.framework.datapermission.core.annotation.DataPermission; |
|||
import com.alibaba.ttl.TransmittableThreadLocal; |
|||
|
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* {@link DataPermission} 注解的 Context 上下文 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class DataPermissionContextHolder { |
|||
|
|||
/** |
|||
* 使用 List 的原因,可能存在方法的嵌套调用 |
|||
*/ |
|||
private static final ThreadLocal<LinkedList<DataPermission>> DATA_PERMISSIONS = |
|||
TransmittableThreadLocal.withInitial(LinkedList::new); |
|||
|
|||
/** |
|||
* 获得当前的 DataPermission 注解 |
|||
* |
|||
* @return DataPermission 注解 |
|||
*/ |
|||
public static DataPermission get() { |
|||
return DATA_PERMISSIONS.get().peekLast(); |
|||
} |
|||
|
|||
/** |
|||
* 入栈 DataPermission 注解 |
|||
* |
|||
* @param dataPermission DataPermission 注解 |
|||
*/ |
|||
public static void add(DataPermission dataPermission) { |
|||
DATA_PERMISSIONS.get().addLast(dataPermission); |
|||
} |
|||
|
|||
/** |
|||
* 出栈 DataPermission 注解 |
|||
* |
|||
* @return DataPermission 注解 |
|||
*/ |
|||
public static DataPermission remove() { |
|||
DataPermission dataPermission = DATA_PERMISSIONS.get().removeLast(); |
|||
// 无元素时,清空 ThreadLocal
|
|||
if (DATA_PERMISSIONS.get().isEmpty()) { |
|||
DATA_PERMISSIONS.remove(); |
|||
} |
|||
return dataPermission; |
|||
} |
|||
|
|||
/** |
|||
* 获得所有 DataPermission |
|||
* |
|||
* @return DataPermission 队列 |
|||
*/ |
|||
public static List<DataPermission> getAll() { |
|||
return DATA_PERMISSIONS.get(); |
|||
} |
|||
|
|||
/** |
|||
* 清空上下文 |
|||
* |
|||
* 目前仅仅用于单测 |
|||
*/ |
|||
public static void clear() { |
|||
DATA_PERMISSIONS.remove(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,57 @@ |
|||
package com.win.framework.datapermission.core.db; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import com.win.framework.datapermission.core.rule.DataPermissionRule; |
|||
import com.win.framework.datapermission.core.rule.DataPermissionRuleFactory; |
|||
import com.win.framework.mybatis.core.util.MyBatisUtils; |
|||
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler; |
|||
import lombok.RequiredArgsConstructor; |
|||
import net.sf.jsqlparser.expression.Expression; |
|||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression; |
|||
import net.sf.jsqlparser.schema.Table; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 基于 {@link DataPermissionRule} 的数据权限处理器 |
|||
* |
|||
* 它的底层,是基于 MyBatis Plus 的 <a href="https://baomidou.com/plugins/data-permission/">数据权限插件</a> |
|||
* 核心原理:它会在 SQL 执行前拦截 SQL 语句,并根据用户权限动态添加权限相关的 SQL 片段。这样,只有用户有权限访问的数据才会被查询出来 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@RequiredArgsConstructor |
|||
public class DataPermissionRuleHandler implements MultiDataPermissionHandler { |
|||
|
|||
private final DataPermissionRuleFactory ruleFactory; |
|||
|
|||
@Override |
|||
public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) { |
|||
// 获得 Mapper 对应的数据权限的规则
|
|||
List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(mappedStatementId); |
|||
if (CollUtil.isEmpty(rules)) { |
|||
return null; |
|||
} |
|||
|
|||
// 生成条件
|
|||
Expression allExpression = null; |
|||
for (DataPermissionRule rule : rules) { |
|||
// 判断表名是否匹配
|
|||
String tableName = MyBatisUtils.getTableName(table); |
|||
if (!rule.getTableNames().contains(tableName)) { |
|||
continue; |
|||
} |
|||
|
|||
// 单条规则的条件
|
|||
Expression oneExpress = rule.getExpression(tableName, table.getAlias()); |
|||
if (oneExpress == null) { |
|||
continue; |
|||
} |
|||
// 拼接到 allExpression 中
|
|||
allExpression = allExpression == null ? oneExpress |
|||
: new AndExpression(allExpression, oneExpress); |
|||
} |
|||
return allExpression; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.win.framework.datapermission.core.rule; |
|||
|
|||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; |
|||
import net.sf.jsqlparser.expression.Alias; |
|||
import net.sf.jsqlparser.expression.Expression; |
|||
|
|||
import java.util.Set; |
|||
|
|||
/** |
|||
* 数据权限规则接口 |
|||
* 通过实现接口,自定义数据规则。例如说, |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public interface DataPermissionRule { |
|||
|
|||
/** |
|||
* 返回需要生效的表名数组 |
|||
* 为什么需要该方法?Data Permission 数组基于 SQL 重写,通过 Where 返回只有权限的数据 |
|||
* |
|||
* 如果需要基于实体名获得表名,可调用 {@link TableInfoHelper#getTableInfo(Class)} 获得 |
|||
* |
|||
* @return 表名数组 |
|||
*/ |
|||
Set<String> getTableNames(); |
|||
|
|||
/** |
|||
* 根据表名和别名,生成对应的 WHERE / OR 过滤条件 |
|||
* |
|||
* @param tableName 表名 |
|||
* @param tableAlias 别名,可能为空 |
|||
* @return 过滤条件 Expression 表达式 |
|||
*/ |
|||
Expression getExpression(String tableName, Alias tableAlias); |
|||
|
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue