first commit
This commit is contained in:
commit
3116c7f7b0
|
@ -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,368 @@
|
|||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/Spring%20Boot-2.7.18-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"/>
|
||||
</p>
|
||||
|
||||
**严肃声明:现在、未来都不会有商业版本,所有代码全部开源!!**
|
||||
|
||||
**「我喜欢写代码,乐此不疲」**
|
||||
**「我喜欢做开源,以此为乐」**
|
||||
|
||||
我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。
|
||||
|
||||
如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
|
||||
|
||||
## 🐶 新手必读
|
||||
|
||||
* 演示地址【Vue3 + element-plus】:<http://dashboard-vue3.yudao.iocoder.cn>
|
||||
* 演示地址【Vue3 + vben(ant-design-vue)】:<http://dashboard-vben.yudao.iocoder.cn>
|
||||
* 演示地址【Vue2 + element-ui】:<http://dashboard.yudao.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/) 分支 |
|
||||
| 【精简版】[yudao-boot-mini](https://gitee.com/yudaocode/yudao-boot-mini) | [`master`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master-jdk17/) 分支 |
|
||||
|
||||
* 【完整版】:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能
|
||||
* 【精简版】:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能
|
||||
|
||||
可参考 [《迁移文档》](https://doc.iocoder.cn/migrate-module/) ,只需要 5-10 分钟,即可将【完整版】按需迁移到【精简版】
|
||||
|
||||
## 🐯 平台简介
|
||||
|
||||
**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
|
||||
|
||||
> 有任何问题,或者想要的功能,可以在 _Issues_ 中提给艿艿。
|
||||
>
|
||||
> 😜 给项目点点 Star 吧,这对我们真的很重要!
|
||||
|
||||

|
||||
|
||||
* 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、阿里云、腾讯云、七牛云等云存储服务
|
||||
* 集成报表设计器、大屏设计器,通过拖拽即可生成酷炫的报表与大屏
|
||||
|
||||
## 🐳 项目关系
|
||||
|
||||

|
||||
|
||||
三个项目的功能对比,可见社区共同整理的 [国产开源项目对比](https://www.yuque.com/xiatian-bsgny/lm0ec1/wqf8mn) 表格。
|
||||
|
||||
### 后端项目
|
||||
|
||||
| 项目 | Star | 简介 |
|
||||
|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|
|
||||
| [ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [](https://gitee.com/zhijiantianya/ruoyi-vue-pro) [](https://github.com/YunaiV/ruoyi-vue-pro) | 基于 Spring Boot 多模块架构 |
|
||||
| [yudao-cloud](https://gitee.com/zhijiantianya/yudao-cloud) | [](https://gitee.com/zhijiantianya/yudao-cloud) [](https://github.com/YunaiV/yudao-cloud) | 基于 Spring Cloud 微服务架构 |
|
||||
| [Spring-Boot-Labs](https://gitee.com/yudaocode/SpringBoot-Labs) | [](https://gitee.com/zhijiantianya/yudao-cloud) [](https://github.com/yudaocode/SpringBoot-Labs) | 系统学习 Spring Boot & Cloud 专栏 |
|
||||
|
||||
### 前端项目
|
||||
|
||||
| 项目 | Star | 简介 |
|
||||
|----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
|
||||
| [yudao-ui-admin-vue3](https://gitee.com/yudaocode/yudao-ui-admin-vue3) | [](https://gitee.com/yudaocode/yudao-ui-admin-vue3) [](https://github.com/yudaocode/yudao-ui-admin-vue3) | 基于 Vue3 + element-plus 实现的管理后台 |
|
||||
| [yudao-ui-admin-vben](https://gitee.com/yudaocode/yudao-ui-admin-vben) | [](https://gitee.com/yudaocode/yudao-ui-admin-vben) [](https://github.com/yudaocode/yudao-ui-admin-vben) | 基于 Vue3 + vben(ant-design-vue) 实现的管理后台 |
|
||||
| [yudao-mall-uniapp](https://gitee.com/yudaocode/yudao-mall-uniapp) | [](https://gitee.com/yudaocode/yudao-mall-uniapp) [](https://github.com/yudaocode/yudao-mall-uniapp) | 基于 uni-app 实现的商城小程序 |
|
||||
| [yudao-ui-admin-vue2](https://gitee.com/yudaocode/yudao-ui-admin-vue2) | [](https://gitee.com/yudaocode/yudao-ui-admin-vue2) [](https://github.com/yudaocode/yudao-ui-admin-vue2) | 基于 Vue2 + element-ui 实现的管理后台 |
|
||||
| [yudao-ui-admin-uniapp](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) | [](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) [](https://github.com/yudaocode/yudao-ui-admin-uniapp) | 基于 Vue2 + element-ui 实现的管理后台 |
|
||||
| [yudao-ui-go-view](https://gitee.com/yudaocode/yudao-ui-go-view) | [](https://gitee.com/yudaocode/yudao-ui-go-view) [](https://github.com/yudaocode/yudao-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)
|
||||
|
||||

|
||||
|
||||
③ 代码整洁、架构整洁,遵循《阿里巴巴 Java 开发手册》规范,代码注释详细,113770 行 Java 代码,42462 行代码注释。
|
||||
|
||||
## 🤝 项目外包
|
||||
|
||||
我们也是接外包滴,如果你有项目想要外包,可以微信联系【**Aix9975**】。
|
||||
|
||||
团队包含专业的项目经理、架构师、前端工程师、后端工程师、测试工程师、运维工程师,可以提供全流程的外包服务。
|
||||
|
||||
项目可以是商城、SCRM 系统、OA 系统、物流系统、ERP 系统、CMS 系统、HIS 系统、支付系统、IM 聊天、微信公众号、微信小程序等等。
|
||||
|
||||
## 🐼 内置功能
|
||||
|
||||
系统内置多种多种业务功能,可以用于快速你的业务系统:
|
||||
|
||||

|
||||
|
||||
* 通用模块(必选):系统功能、基础设施
|
||||
* 通用模块(可选):工作流程、支付系统、数据报表、会员中心
|
||||
* 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号、AI 大模型
|
||||
|
||||
> 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
|
||||
>
|
||||
> * 额外新增的功能,我们使用 🚀 标记。
|
||||
> * 重新实现的功能,我们使用 ⭐️ 标记。
|
||||
|
||||
🙂 所有功能,都通过 **单元测试** 保证高质量。
|
||||
|
||||
### 系统功能
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|-----|-------|---------------------------------|
|
||||
| | 用户管理 | 用户是系统操作者,该功能主要完成系统用户配置 |
|
||||
| ⭐️ | 在线用户 | 当前系统中活跃用户状态监控,支持手动踢下线 |
|
||||
| | 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 |
|
||||
| | 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能 |
|
||||
| | 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 |
|
||||
| | 岗位管理 | 配置系统用户所属担任职务 |
|
||||
| 🚀 | 租户管理 | 配置系统租户,支持 SaaS 场景下的多租户功能 |
|
||||
| 🚀 | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 |
|
||||
| | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 |
|
||||
| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
|
||||
| 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 |
|
||||
| 🚀 | 站内信 | 系统内的消息通知,提供站内信模版、站内信消息 |
|
||||
| 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
|
||||
| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 |
|
||||
| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |
|
||||
| | 通知公告 | 系统通知公告信息发布维护 |
|
||||
| 🚀 | 敏感词 | 配置系统敏感词,支持标签分组 |
|
||||
| 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
|
||||
| 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 |
|
||||
|
||||

|
||||
|
||||
### 工作流程
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|----|-------|-----------------------------------------|
|
||||
| 🚀 | 流程模型 | 配置工作流的流程模型,支持 BPMN 和仿钉钉/飞书设计器 |
|
||||
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
|
||||
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
|
||||
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
|
||||
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转派、委派、退回、加减签等操作 |
|
||||
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,支持流程预测,展示未来审批人信息 |
|
||||
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
|
||||
|
||||

|
||||
|
||||
| BPMN 设计器 | 钉钉/飞书设计器 |
|
||||
|------------------------------|--------------------------------|
|
||||
|  |  |
|
||||
|
||||
### 支付系统
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|-----|------|---------------------------|
|
||||
| 🚀 | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 |
|
||||
| 🚀 | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单 |
|
||||
| 🚀 | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单 |
|
||||
| 🚀 | 回调通知 | 查看支付回调业务的【支付】【退款】的通知结果 |
|
||||
| 🚀 | 接入示例 | 提供接入支付系统的【支付】【退款】的功能实战 |
|
||||
|
||||
### 基础设施
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|-----|-----------|----------------------------------------------|
|
||||
| 🚀 | 代码生成 | 前后端代码的生成(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 实现单元测试,保证功能的正确性、代码的质量等 |
|
||||
|
||||

|
||||
|
||||
### 数据报表
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|-----|-------|--------------------|
|
||||
| 🚀 | 报表设计器 | 支持数据报表、图形报表、打印设计等 |
|
||||
| 🚀 | 大屏设计器 | 拖拽生成数据大屏,内置几十种图表组件 |
|
||||
|
||||
### 微信公众号
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|-----|--------|-------------------------------|
|
||||
| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 |
|
||||
| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 |
|
||||
| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 |
|
||||
| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 |
|
||||
| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 |
|
||||
| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 |
|
||||
| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 |
|
||||
| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 |
|
||||
| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 |
|
||||
| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 |
|
||||
|
||||
### 商城系统
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/mall-preview/>
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 会员中心
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|-----|------|----------------------------------|
|
||||
| 🚀 | 会员管理 | 会员是 C 端的消费者,该功能用于会员的搜索与管理 |
|
||||
| 🚀 | 会员标签 | 对会员的标签进行创建、查询、修改、删除等操作 |
|
||||
| 🚀 | 会员等级 | 对会员的等级、成长值进行管理,可用于订单折扣等会员权益 |
|
||||
| 🚀 | 会员分组 | 对会员进行分组,用于用户画像、内容推送等运营手段 |
|
||||
| 🚀 | 积分签到 | 回馈给签到、消费等行为的积分,会员可订单抵现、积分兑换等途径消耗 |
|
||||
|
||||
### ERP 系统
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/erp-preview/>
|
||||
|
||||

|
||||
|
||||
### CRM 系统
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/crm-preview/>
|
||||
|
||||

|
||||
|
||||
### AI 大模型
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/ai-preview/>
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 🐨 技术栈
|
||||
|
||||
### 模块
|
||||
|
||||
| 项目 | 说明 |
|
||||
|-----------------------|--------------------|
|
||||
| `yudao-dependencies` | Maven 依赖版本管理 |
|
||||
| `yudao-framework` | Java 框架拓展 |
|
||||
| `yudao-server` | 管理后台 + 用户 APP 的服务端 |
|
||||
| `yudao-module-system` | 系统功能的 Module 模块 |
|
||||
| `yudao-module-member` | 会员中心的 Module 模块 |
|
||||
| `yudao-module-infra` | 基础设施的 Module 模块 |
|
||||
| `yudao-module-bpm` | 工作流程的 Module 模块 |
|
||||
| `yudao-module-pay` | 支付系统的 Module 模块 |
|
||||
| `yudao-module-mall` | 商城系统的 Module 模块 |
|
||||
| `yudao-module-erp` | ERP 系统的 Module 模块 |
|
||||
| `yudao-module-crm` | CRM 系统的 Module 模块 |
|
||||
| `yudao-module-ai` | AI 大模型的 Module 模块 |
|
||||
| `yudao-module-mp` | 微信公众号的 Module 模块 |
|
||||
| `yudao-module-report` | 大屏报表 Module 模块 |
|
||||
|
||||
### 框架
|
||||
|
||||
| 框架 | 说明 | 版本 | 学习指南 |
|
||||
|---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------|
|
||||
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.18 | [文档](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/?yudao) |
|
||||
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.7 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
|
||||
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.6.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
|
||||
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 /7.0 | |
|
||||
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.32.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
|
||||
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.3.24 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
|
||||
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.7.11 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
|
||||
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.2.5 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
|
||||
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 6.8.0 | [文档](https://doc.iocoder.cn/bpm/) |
|
||||
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
|
||||
| [Springdoc](https://springdoc.org/) | Swagger 文档 | 1.7.0 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
|
||||
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 8.12.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
|
||||
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 2.7.10 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
|
||||
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.13.5 | |
|
||||
| [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.6.3 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
|
||||
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.34 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
|
||||
| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.8.2 | - |
|
||||
| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 4.8.0 | - |
|
||||
|
||||
## 🐷 演示图
|
||||
|
||||
### 系统功能
|
||||
|
||||
| 模块 | biu | biu | biu |
|
||||
|----------|-----------------------------|---------------------------|--------------------------|
|
||||
| 登录 & 首页 |  |  |  |
|
||||
| 用户 & 应用 |  |  |  |
|
||||
| 租户 & 套餐 |  |  | - |
|
||||
| 部门 & 岗位 |  |  | - |
|
||||
| 菜单 & 角色 |  |  | - |
|
||||
| 审计日志 |  |  | - |
|
||||
| 短信 |  |  |  |
|
||||
| 字典 & 敏感词 |  |  |  |
|
||||
| 错误码 & 通知 |  |  | - |
|
||||
|
||||
### 工作流程
|
||||
|
||||
| 模块 | biu | biu | biu |
|
||||
|---------|---------------------------------|---------------------------------|---------------------------------|
|
||||
| 流程模型 |  |  |  |
|
||||
| 表单 & 分组 |  |  | - |
|
||||
| 我的流程 |  |  |  |
|
||||
| 待办 & 已办 |  |  |  |
|
||||
| OA 请假 |  |  |  |
|
||||
|
||||
### 基础设施
|
||||
|
||||
| 模块 | biu | biu | biu |
|
||||
|---------------|-------------------------------|-----------------------------|---------------------------|
|
||||
| 代码生成 |  |  | - |
|
||||
| 文档 |  |  | - |
|
||||
| 文件 & 配置 |  |  |  |
|
||||
| 定时任务 |  |  | - |
|
||||
| API 日志 |  |  | - |
|
||||
| MySQL & Redis |  |  | - |
|
||||
| 监控平台 |  |  |  |
|
||||
|
||||
### 支付系统
|
||||
|
||||
| 模块 | biu | biu | biu |
|
||||
|---------|---------------------------|---------------------------------|---------------------------------|
|
||||
| 商家 & 应用 |  |  |  |
|
||||
| 支付 & 退款 |  |  | --- |
|
||||
### 数据报表
|
||||
|
||||
| 模块 | biu | biu | biu |
|
||||
|-------|---------------------------------|---------------------------------|---------------------------------------|
|
||||
| 报表设计器 |  |  |  |
|
||||
| 大屏设计器 |  |  |  |
|
||||
|
||||
### 移动端(管理后台)
|
||||
|
||||
| biu | biu | biu |
|
||||
|----------------------------------|----------------------------------|----------------------------------|
|
||||
|  |  |  |
|
||||
|  |  |  |
|
||||
|  |  |  |
|
||||
|
||||
目前已经实现登录、我的、工作台、编辑资料、头像修改、密码修改、常见问题、关于我们等基础功能。
|
|
@ -0,0 +1,4 @@
|
|||
config.stopBubbling = true
|
||||
lombok.tostring.callsuper=CALL
|
||||
lombok.equalsandhashcode.callsuper=CALL
|
||||
lombok.accessors.chain=true
|
|
@ -0,0 +1,167 @@
|
|||
<?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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao</artifactId>
|
||||
<version>${revision}</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>yudao-dependencies</module>
|
||||
<module>yudao-framework</module>
|
||||
<!-- Server 主项目 -->
|
||||
<module>yudao-server</module>
|
||||
<!-- 各种 module 拓展 -->
|
||||
<module>yudao-module-system</module>
|
||||
<module>yudao-module-infra</module>
|
||||
<module>yudao-module-member</module>
|
||||
<module>yudao-module-bpm</module>
|
||||
<module>yudao-module-report</module>
|
||||
<module>yudao-module-mp</module>
|
||||
<module>yudao-module-pay</module>
|
||||
<module>yudao-module-mall</module>
|
||||
<module>yudao-module-crm</module>
|
||||
<module>yudao-module-erp</module>
|
||||
<!-- <module>yudao-module-iot</module>-->
|
||||
<!-- AI 大模型的开启,请参考 https://doc.iocoder.cn/ai/build/ 文档,对 JDK 版本要要求! -->
|
||||
<!-- <module>yudao-module-ai</module>-->
|
||||
</modules>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>芋道项目基础脚手架</description>
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.4.1-jdk8-SNAPSHOT</revision>
|
||||
<!-- Maven 相关 -->
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
|
||||
<maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
|
||||
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
|
||||
<!-- 看看咋放到 bom 里 -->
|
||||
<lombok.version>1.18.36</lombok.version>
|
||||
<spring.boot.version>2.7.18</spring.boot.version>
|
||||
<mapstruct.version>1.6.3</mapstruct.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-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>
|
||||
</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: yudao-system`,降低`version`版本为`3.3`以下,支持`docker-compose`。
|
||||
|
||||
## 功能文件列表
|
||||
|
||||
```text
|
||||
.
|
||||
├── Docker-HOWTO.md
|
||||
├── docker-compose.yml
|
||||
├── docker.env <-- 提供docker-compose环境变量配置
|
||||
├── yudao-server
|
||||
│ └── Dockerfile
|
||||
└── yudao-ui-admin
|
||||
├── .dockerignore
|
||||
├── Dockerfile
|
||||
└── nginx.conf <-- 提供基础配置,gzip压缩、api转发
|
||||
```
|
||||
|
||||
## 构建 jar 包
|
||||
|
||||
```shell
|
||||
# 创建maven缓存volume
|
||||
docker volume create --name yudao-maven-repo
|
||||
|
||||
docker run -it --rm --name yudao-maven \
|
||||
-v yudao-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: yudao-system
|
||||
|
||||
services:
|
||||
mysql:
|
||||
container_name: yudao-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: yudao-redis
|
||||
image: redis:6-alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis:/data
|
||||
|
||||
server:
|
||||
container_name: yudao-server
|
||||
build:
|
||||
context: ./yudao-server/
|
||||
image: yudao-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://yudao-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://yudao-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.redis.host=${REDIS_HOST:-yudao-redis}
|
||||
depends_on:
|
||||
- mysql
|
||||
- redis
|
||||
|
||||
admin:
|
||||
container_name: yudao-admin
|
||||
build:
|
||||
context: ./yudao-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: yudao-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://yudao-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=yudao-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",
|
||||
"adminTenantId": "1",
|
||||
|
||||
"appApi": "http://127.0.0.1:48080/app-api",
|
||||
"appToken": "test247",
|
||||
"appTenantId": "1"
|
||||
},
|
||||
"gateway": {
|
||||
"baseUrl": "http://127.0.0.1:8888/admin-api",
|
||||
"token": "test1",
|
||||
"adminTenantId": "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 = 'yudao-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/yudao-server
|
||||
# 编译后 jar 的地址。部署时,Jenkins 会上传 jar 包到该目录下
|
||||
SOURCE_PATH=$BASE_PATH/build
|
||||
# 服务名称。同时约定部署服务的 jar 包名字也为它。
|
||||
SERVER_NAME=yudao-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
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
暂未适配 IBM DB2 数据库,如果你有需要,可以微信联系 wangwenbin-server 一起建设。
|
||||
|
||||
你需要把表结构与数据导入到 DM 数据库,我来测试与适配代码。
|
|
@ -0,0 +1,598 @@
|
|||
package liquibase.database.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import liquibase.CatalogAndSchema;
|
||||
import liquibase.Scope;
|
||||
import liquibase.database.AbstractJdbcDatabase;
|
||||
import liquibase.database.DatabaseConnection;
|
||||
import liquibase.database.OfflineConnection;
|
||||
import liquibase.database.jvm.JdbcConnection;
|
||||
import liquibase.exception.DatabaseException;
|
||||
import liquibase.exception.UnexpectedLiquibaseException;
|
||||
import liquibase.exception.ValidationErrors;
|
||||
import liquibase.executor.ExecutorService;
|
||||
import liquibase.statement.DatabaseFunction;
|
||||
import liquibase.statement.SequenceCurrentValueFunction;
|
||||
import liquibase.statement.SequenceNextValueFunction;
|
||||
import liquibase.statement.core.RawCallStatement;
|
||||
import liquibase.statement.core.RawSqlStatement;
|
||||
import liquibase.structure.DatabaseObject;
|
||||
import liquibase.structure.core.Catalog;
|
||||
import liquibase.structure.core.Index;
|
||||
import liquibase.structure.core.PrimaryKey;
|
||||
import liquibase.structure.core.Schema;
|
||||
import liquibase.util.JdbcUtils;
|
||||
import liquibase.util.StringUtil;
|
||||
|
||||
public class DmDatabase extends AbstractJdbcDatabase {
|
||||
private static final String PRODUCT_NAME = "DM DBMS";
|
||||
|
||||
@Override
|
||||
protected String getDefaultDatabaseProductName() {
|
||||
return PRODUCT_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this AbstractDatabase subclass the correct one to use for the given connection.
|
||||
*
|
||||
* @param conn
|
||||
*/
|
||||
@Override
|
||||
public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {
|
||||
return PRODUCT_NAME.equalsIgnoreCase(conn.getDatabaseProductName());
|
||||
}
|
||||
|
||||
/**
|
||||
* If this database understands the given url, return the default driver class name. Otherwise return null.
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
@Override
|
||||
public String getDefaultDriver(String url) {
|
||||
if(url.startsWith("jdbc:dm")) {
|
||||
return "dm.jdbc.driver.DmDriver";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an all-lower-case short name of the product. Used for end-user selecting of database type
|
||||
* such as the DBMS precondition.
|
||||
*/
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return "dm";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getDefaultPort() {
|
||||
return 5236;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this database support initially deferrable columns.
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsInitiallyDeferrableColumns() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTablespaces() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return PRIORITY_DEFAULT;
|
||||
}
|
||||
|
||||
private static final Pattern PROXY_USER = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*");
|
||||
|
||||
protected final int SHORT_IDENTIFIERS_LENGTH = 30;
|
||||
protected final int LONG_IDENTIFIERS_LEGNTH = 128;
|
||||
public static final int ORACLE_12C_MAJOR_VERSION = 12;
|
||||
|
||||
private Set<String> reservedWords = new HashSet<>();
|
||||
private Set<String> userDefinedTypes;
|
||||
private Map<String, String> savedSessionNlsSettings;
|
||||
|
||||
private Boolean canAccessDbaRecycleBin;
|
||||
private Integer databaseMajorVersion;
|
||||
private Integer databaseMinorVersion;
|
||||
|
||||
/**
|
||||
* Default constructor for an object that represents the Oracle Database DBMS.
|
||||
*/
|
||||
public DmDatabase() {
|
||||
super.unquotedObjectsAreUppercased = true;
|
||||
//noinspection HardCodedStringLiteral
|
||||
super.setCurrentDateTimeFunction("SYSTIMESTAMP");
|
||||
// Setting list of Oracle's native functions
|
||||
//noinspection HardCodedStringLiteral
|
||||
dateFunctions.add(new DatabaseFunction("SYSDATE"));
|
||||
//noinspection HardCodedStringLiteral
|
||||
dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP"));
|
||||
//noinspection HardCodedStringLiteral
|
||||
dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP"));
|
||||
//noinspection HardCodedStringLiteral
|
||||
super.sequenceNextValueFunction = "%s.nextval";
|
||||
//noinspection HardCodedStringLiteral
|
||||
super.sequenceCurrentValueFunction = "%s.currval";
|
||||
}
|
||||
|
||||
private void tryProxySession(final String url, final Connection con) {
|
||||
Matcher m = PROXY_USER.matcher(url);
|
||||
if (m.matches()) {
|
||||
Properties props = new Properties();
|
||||
props.put("PROXY_USER_NAME", m.group(1));
|
||||
try {
|
||||
Method method = con.getClass().getMethod("openProxySession", int.class, Properties.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(con, 1, props);
|
||||
} catch (Exception e) {
|
||||
Scope.getCurrentScope().getLog(getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDatabaseMajorVersion() throws DatabaseException {
|
||||
if (databaseMajorVersion == null) {
|
||||
return super.getDatabaseMajorVersion();
|
||||
} else {
|
||||
return databaseMajorVersion;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDatabaseMinorVersion() throws DatabaseException {
|
||||
if (databaseMinorVersion == null) {
|
||||
return super.getDatabaseMinorVersion();
|
||||
} else {
|
||||
return databaseMinorVersion;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcCatalogName(CatalogAndSchema schema) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcSchemaName(CatalogAndSchema schema) {
|
||||
return correctObjectName((schema.getCatalogName() == null) ? schema.getSchemaName() : schema.getCatalogName(), Schema.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAutoIncrementClause(final String generationType, final Boolean defaultOnNull) {
|
||||
if (StringUtil.isEmpty(generationType)) {
|
||||
return super.getAutoIncrementClause();
|
||||
}
|
||||
|
||||
String autoIncrementClause = "GENERATED %s AS IDENTITY"; // %s -- [ ALWAYS | BY DEFAULT [ ON NULL ] ]
|
||||
String generationStrategy = generationType;
|
||||
if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) {
|
||||
generationStrategy += " ON NULL";
|
||||
}
|
||||
return String.format(autoIncrementClause, generationStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generatePrimaryKeyName(String tableName) {
|
||||
if (tableName.length() > 27) {
|
||||
//noinspection HardCodedStringLiteral
|
||||
return "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27);
|
||||
} else {
|
||||
//noinspection HardCodedStringLiteral
|
||||
return "PK_" + tableName.toUpperCase(Locale.US);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReservedWord(String objectName) {
|
||||
return reservedWords.contains(objectName.toUpperCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSequences() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle supports catalogs in liquibase terms
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsSchemas() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getConnectionCatalogName() throws DatabaseException {
|
||||
if (getConnection() instanceof OfflineConnection) {
|
||||
return getConnection().getCatalog();
|
||||
}
|
||||
try {
|
||||
//noinspection HardCodedStringLiteral
|
||||
return Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class);
|
||||
} catch (Exception e) {
|
||||
//noinspection HardCodedStringLiteral
|
||||
Scope.getCurrentScope().getLog(getClass()).info("Error getting default schema", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultCatalogName() {//NOPMD
|
||||
return (super.getDefaultCatalogName() == null) ? null : super.getDefaultCatalogName().toUpperCase(Locale.US);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns an Oracle date literal with the same value as a string formatted using ISO 8601.</p>
|
||||
*
|
||||
* <p>Convert an ISO8601 date string to one of the following results:
|
||||
* to_date('1995-05-23', 'YYYY-MM-DD')
|
||||
* to_date('1995-05-23 09:23:59', 'YYYY-MM-DD HH24:MI:SS')</p>
|
||||
* <p>
|
||||
* Implementation restriction:<br>
|
||||
* Currently, only the following subsets of ISO8601 are supported:<br>
|
||||
* <ul>
|
||||
* <li>YYYY-MM-DD</li>
|
||||
* <li>YYYY-MM-DDThh:mm:ss</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public String getDateLiteral(String isoDate) {
|
||||
String normalLiteral = super.getDateLiteral(isoDate);
|
||||
|
||||
if (isDateOnly(isoDate)) {
|
||||
return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')";
|
||||
} else if (isTimeOnly(isoDate)) {
|
||||
return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')";
|
||||
} else if (isTimestamp(isoDate)) {
|
||||
return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')";
|
||||
} else if (isDateTime(isoDate)) {
|
||||
int seppos = normalLiteral.lastIndexOf('.');
|
||||
if (seppos != -1) {
|
||||
normalLiteral = normalLiteral.substring(0, seppos) + "'";
|
||||
}
|
||||
return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')";
|
||||
}
|
||||
return "UNSUPPORTED:" + isoDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystemObject(DatabaseObject example) {
|
||||
if (example == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isLiquibaseObject(example)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (example instanceof Schema) {
|
||||
//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
|
||||
if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) {
|
||||
return true;
|
||||
}
|
||||
//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
|
||||
if ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) {
|
||||
return true;
|
||||
}
|
||||
} else if (isSystemObject(example.getSchema())) {
|
||||
return true;
|
||||
}
|
||||
if (example instanceof Catalog) {
|
||||
//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
|
||||
if (("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName()))) {
|
||||
return true;
|
||||
}
|
||||
} else if (example.getName() != null) {
|
||||
//noinspection HardCodedStringLiteral
|
||||
if (example.getName().startsWith("BIN$")) { //oracle deleted table
|
||||
boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin();
|
||||
if (!filteredInOriginalQuery) {
|
||||
filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName());
|
||||
}
|
||||
|
||||
if (filteredInOriginalQuery) {
|
||||
return !((example instanceof PrimaryKey) || (example instanceof Index) || (example instanceof
|
||||
liquibase.statement.UniqueConstraint));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if (example.getName().startsWith("AQ$")) { //oracle AQ tables
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if (example.getName().startsWith("DR$")) { //oracle index tables
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if (example.getName().startsWith("SYS_IOT_OVER")) { //oracle system table
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral,HardCodedStringLiteral
|
||||
if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) {
|
||||
// CORE-1768 - Oracle creates these for spatial indices and will remove them when the index is removed.
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if (example.getName().startsWith("MLOG$_")) { //Created by materliaized view logs for every table that is part of a materialized view. Not available for DDL operations.
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if (example.getName().startsWith("RUPD$_")) { //Created by materialized view log tables using primary keys. Not available for DDL operations.
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if (example.getName().startsWith("WM$_")) { //Workspace Manager backup tables.
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) { //This table contains the name of the Java object, the date it was loaded, and has a BLOB column to store the Java object.
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) { //This is a hash table that tracks the loading of Java objects into a schema.
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if (example.getName().startsWith("ISEQ$$_")) { //System-generated sequence
|
||||
return true;
|
||||
} else //noinspection HardCodedStringLiteral
|
||||
if (example.getName().startsWith("USLOG$")) { //for update materialized view
|
||||
return true;
|
||||
} else if (example.getName().startsWith("SYS_FBA")) { //for Flashback tables
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.isSystemObject(example);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAutoIncrement() {
|
||||
// Oracle supports Identity beginning with version 12c
|
||||
boolean isAutoIncrementSupported = false;
|
||||
|
||||
try {
|
||||
if (getDatabaseMajorVersion() >= 12) {
|
||||
isAutoIncrementSupported = true;
|
||||
}
|
||||
|
||||
// Returning true will generate create table command with 'IDENTITY' clause, example:
|
||||
// CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) GENERATED BY DEFAULT AS IDENTITY NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey));
|
||||
|
||||
// While returning false will continue to generate create table command without 'IDENTITY' clause, example:
|
||||
// CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey));
|
||||
|
||||
} catch (DatabaseException ex) {
|
||||
isAutoIncrementSupported = false;
|
||||
}
|
||||
|
||||
return isAutoIncrementSupported;
|
||||
}
|
||||
|
||||
|
||||
// public Set<UniqueConstraint> findUniqueConstraints(String schema) throws DatabaseException {
|
||||
// Set<UniqueConstraint> returnSet = new HashSet<UniqueConstraint>();
|
||||
//
|
||||
// List<Map> maps = new Executor(this).queryForList(new RawSqlStatement("SELECT UC.CONSTRAINT_NAME, UCC.TABLE_NAME, UCC.COLUMN_NAME FROM USER_CONSTRAINTS UC, USER_CONS_COLUMNS UCC WHERE UC.CONSTRAINT_NAME=UCC.CONSTRAINT_NAME AND CONSTRAINT_TYPE='U' ORDER BY UC.CONSTRAINT_NAME"));
|
||||
//
|
||||
// UniqueConstraint constraint = null;
|
||||
// for (Map map : maps) {
|
||||
// if (constraint == null || !constraint.getName().equals(constraint.getName())) {
|
||||
// returnSet.add(constraint);
|
||||
// Table table = new Table((String) map.get("TABLE_NAME"));
|
||||
// constraint = new UniqueConstraint(map.get("CONSTRAINT_NAME").toString(), table);
|
||||
// }
|
||||
// }
|
||||
// if (constraint != null) {
|
||||
// returnSet.add(constraint);
|
||||
// }
|
||||
//
|
||||
// return returnSet;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean supportsRestrictForeignKeys() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDataTypeMaxParameters(String dataTypeName) {
|
||||
//noinspection HardCodedStringLiteral
|
||||
if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) {
|
||||
return 0;
|
||||
}
|
||||
//noinspection HardCodedStringLiteral
|
||||
if ("BINARY_DOUBLE".equals(dataTypeName.toUpperCase())) {
|
||||
return 0;
|
||||
}
|
||||
return super.getDataTypeMaxParameters(dataTypeName);
|
||||
}
|
||||
|
||||
public String getSystemTableWhereClause(String tableNameColumn) {
|
||||
List<String> clauses = new ArrayList<String>(Arrays.asList("BIN$",
|
||||
"AQ$",
|
||||
"DR$",
|
||||
"SYS_IOT_OVER",
|
||||
"MLOG$_",
|
||||
"RUPD$_",
|
||||
"WM$_",
|
||||
"ISEQ$$_",
|
||||
"USLOG$",
|
||||
"SYS_FBA"));
|
||||
|
||||
for (int i = 0;i<clauses.size(); i++) {
|
||||
clauses.set(i, tableNameColumn+" NOT LIKE '"+clauses.get(i)+"%'");
|
||||
}
|
||||
return "("+ StringUtil.join(clauses, " AND ") + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean jdbcCallsCatalogsSchemas() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<String> getUserDefinedTypes() {
|
||||
if (userDefinedTypes == null) {
|
||||
userDefinedTypes = new HashSet<>();
|
||||
if ((getConnection() != null) && !(getConnection() instanceof OfflineConnection)) {
|
||||
try {
|
||||
try {
|
||||
//noinspection HardCodedStringLiteral
|
||||
userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class));
|
||||
} catch (DatabaseException e) { //fall back to USER_TYPES if the user cannot see ALL_TYPES
|
||||
//noinspection HardCodedStringLiteral
|
||||
userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class));
|
||||
}
|
||||
} catch (DatabaseException e) {
|
||||
//ignore error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return userDefinedTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) {
|
||||
//noinspection HardCodedStringLiteral
|
||||
if ((databaseFunction != null) && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) {
|
||||
return databaseFunction.toString();
|
||||
}
|
||||
if ((databaseFunction instanceof SequenceNextValueFunction) || (databaseFunction instanceof
|
||||
SequenceCurrentValueFunction)) {
|
||||
String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction);
|
||||
// replace "myschema.my_seq".nextval with "myschema"."my_seq".nextval
|
||||
return quotedSeq.replaceFirst("\"([^\\.\"]+)\\.([^\\.\"]+)\"", "\"$1\".\"$2\"");
|
||||
|
||||
}
|
||||
|
||||
return super.generateDatabaseFunctionValue(databaseFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationErrors validate() {
|
||||
ValidationErrors errors = super.validate();
|
||||
DatabaseConnection connection = getConnection();
|
||||
if ((connection == null) || (connection instanceof OfflineConnection)) {
|
||||
//noinspection HardCodedStringLiteral
|
||||
Scope.getCurrentScope().getLog(getClass()).info("Cannot validate offline database");
|
||||
return errors;
|
||||
}
|
||||
|
||||
if (!canAccessDbaRecycleBin()) {
|
||||
errors.addWarning(getDbaRecycleBinWarning());
|
||||
}
|
||||
|
||||
return errors;
|
||||
|
||||
}
|
||||
|
||||
public String getDbaRecycleBinWarning() {
|
||||
//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,
|
||||
// HardCodedStringLiteral
|
||||
//noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral
|
||||
return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where " +
|
||||
"constraints are deleted and restored. Since Oracle doesn't properly restore the original table names " +
|
||||
"referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this" +
|
||||
" issue.\n" +
|
||||
"\n" +
|
||||
"The user you used to connect to the database (" + getConnection().getConnectionUserName() +
|
||||
") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. " +
|
||||
"Please run the following SQL to set the appropriate permissions, and try running the command again.\n" +
|
||||
"\n" +
|
||||
" GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + getConnection().getConnectionUserName() + ";";
|
||||
}
|
||||
|
||||
public boolean canAccessDbaRecycleBin() {
|
||||
if (canAccessDbaRecycleBin == null) {
|
||||
DatabaseConnection connection = getConnection();
|
||||
if ((connection == null) || (connection instanceof OfflineConnection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Statement statement = null;
|
||||
try {
|
||||
statement = ((JdbcConnection) connection).createStatement();
|
||||
@SuppressWarnings("HardCodedStringLiteral") ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1");
|
||||
resultSet.close(); //don't need to do anything with the result set, just make sure statement ran.
|
||||
this.canAccessDbaRecycleBin = true;
|
||||
} catch (Exception e) {
|
||||
//noinspection HardCodedStringLiteral
|
||||
if ((e instanceof SQLException) && e.getMessage().startsWith("ORA-00942")) { //ORA-00942: table or view does not exist
|
||||
this.canAccessDbaRecycleBin = false;
|
||||
} else {
|
||||
//noinspection HardCodedStringLiteral
|
||||
Scope.getCurrentScope().getLog(getClass()).warning("Cannot check dba_recyclebin access", e);
|
||||
this.canAccessDbaRecycleBin = false;
|
||||
}
|
||||
} finally {
|
||||
JdbcUtils.close(null, statement);
|
||||
}
|
||||
}
|
||||
|
||||
return canAccessDbaRecycleBin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNotNullConstraintNames() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given String would be a valid identifier in Oracle DBMS. In Oracle, a valid identifier has
|
||||
* the following form (case-insensitive comparison):
|
||||
* 1st character: A-Z
|
||||
* 2..n characters: A-Z0-9$_#
|
||||
* The maximum length of an identifier differs by Oracle version and object type.
|
||||
*/
|
||||
public boolean isValidOracleIdentifier(String identifier, Class<? extends DatabaseObject> type) {
|
||||
if ((identifier == null) || (identifier.length() < 1))
|
||||
return false;
|
||||
|
||||
if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$"))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* @todo It seems we currently do not have a class for tablespace identifiers, and all other classes
|
||||
* we do know seem to be supported as 12cR2 long identifiers, so:
|
||||
*/
|
||||
return (identifier.length() <= LONG_IDENTIFIERS_LEGNTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of bytes (NOT: characters) for an identifier. For Oracle <=12c Release 20, this
|
||||
* is 30 bytes, and starting from 12cR2, up to 128 (except for tablespaces, PDB names and some other rather rare
|
||||
* object types).
|
||||
*
|
||||
* @return the maximum length of an object identifier, in bytes
|
||||
*/
|
||||
public int getIdentifierMaximumLength() {
|
||||
try {
|
||||
if (getDatabaseMajorVersion() < ORACLE_12C_MAJOR_VERSION) {
|
||||
return SHORT_IDENTIFIERS_LENGTH;
|
||||
} else if ((getDatabaseMajorVersion() == ORACLE_12C_MAJOR_VERSION) && (getDatabaseMinorVersion() <= 1)) {
|
||||
return SHORT_IDENTIFIERS_LENGTH;
|
||||
} else {
|
||||
return LONG_IDENTIFIERS_LEGNTH;
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package liquibase.datatype.core;
|
||||
|
||||
import liquibase.change.core.LoadDataChange;
|
||||
import liquibase.database.Database;
|
||||
import liquibase.database.core.*;
|
||||
import liquibase.datatype.DataTypeInfo;
|
||||
import liquibase.datatype.DatabaseDataType;
|
||||
import liquibase.datatype.LiquibaseDataType;
|
||||
import liquibase.exception.UnexpectedLiquibaseException;
|
||||
import liquibase.statement.DatabaseFunction;
|
||||
import liquibase.util.StringUtil;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@DataTypeInfo(name = "boolean", aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT)
|
||||
public class BooleanType extends LiquibaseDataType {
|
||||
|
||||
@Override
|
||||
public DatabaseDataType toDatabaseDataType(Database database) {
|
||||
String originalDefinition = StringUtil.trimToEmpty(getRawDefinition());
|
||||
if ((database instanceof Firebird3Database)) {
|
||||
return new DatabaseDataType("BOOLEAN");
|
||||
}
|
||||
|
||||
if ((database instanceof Db2zDatabase) || (database instanceof FirebirdDatabase)) {
|
||||
return new DatabaseDataType("SMALLINT");
|
||||
} else if (database instanceof MSSQLDatabase) {
|
||||
return new DatabaseDataType(database.escapeDataTypeName("bit"));
|
||||
} else if (database instanceof MySQLDatabase) {
|
||||
if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {
|
||||
return new DatabaseDataType("BIT", getParameters());
|
||||
}
|
||||
return new DatabaseDataType("BIT", 1);
|
||||
} else if (database instanceof OracleDatabase) {
|
||||
return new DatabaseDataType("NUMBER", 1);
|
||||
} else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) {
|
||||
return new DatabaseDataType("BIT");
|
||||
} else if (database instanceof DerbyDatabase) {
|
||||
if (((DerbyDatabase) database).supportsBooleanDataType()) {
|
||||
return new DatabaseDataType("BOOLEAN");
|
||||
} else {
|
||||
return new DatabaseDataType("SMALLINT");
|
||||
}
|
||||
} else if (database instanceof DB2Database) {
|
||||
if (((DB2Database) database).supportsBooleanDataType())
|
||||
return new DatabaseDataType("BOOLEAN");
|
||||
else
|
||||
return new DatabaseDataType("SMALLINT");
|
||||
} else if (database instanceof HsqlDatabase) {
|
||||
return new DatabaseDataType("BOOLEAN");
|
||||
} else if (database instanceof PostgresDatabase) {
|
||||
if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {
|
||||
return new DatabaseDataType("BIT", getParameters());
|
||||
}
|
||||
} else if (database instanceof DmDatabase) { // dhb52: DM Support
|
||||
return new DatabaseDataType("bit");
|
||||
}
|
||||
|
||||
return super.toDatabaseDataType(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String objectToSql(Object value, Database database) {
|
||||
if ((value == null) || "null".equals(value.toString().toLowerCase(Locale.US))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String returnValue;
|
||||
if (value instanceof String) {
|
||||
value = ((String) value).replaceAll("'", "");
|
||||
if ("true".equals(((String) value).toLowerCase(Locale.US)) || "1".equals(value) || "b'1'".equals(((String) value).toLowerCase(Locale.US)) || "t".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) {
|
||||
returnValue = this.getTrueBooleanValue(database);
|
||||
} else if ("false".equals(((String) value).toLowerCase(Locale.US)) || "0".equals(value) || "b'0'".equals(
|
||||
((String) value).toLowerCase(Locale.US)) || "f".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) {
|
||||
returnValue = this.getFalseBooleanValue(database);
|
||||
} else if (database instanceof PostgresDatabase && Pattern.matches("b?([01])\\1*(::bit|::\"bit\")?", (String) value)) {
|
||||
returnValue = "b'"
|
||||
+ value.toString()
|
||||
.replace("b", "")
|
||||
.replace("\"", "")
|
||||
.replace("::it", "")
|
||||
+ "'::\"bit\"";
|
||||
} else {
|
||||
throw new UnexpectedLiquibaseException("Unknown boolean value: " + value);
|
||||
}
|
||||
} else if (value instanceof Long) {
|
||||
if (Long.valueOf(1).equals(value)) {
|
||||
returnValue = this.getTrueBooleanValue(database);
|
||||
} else {
|
||||
returnValue = this.getFalseBooleanValue(database);
|
||||
}
|
||||
} else if (value instanceof Number) {
|
||||
if (value.equals(1) || "1".equals(value.toString()) || "1.0".equals(value.toString())) {
|
||||
returnValue = this.getTrueBooleanValue(database);
|
||||
} else {
|
||||
returnValue = this.getFalseBooleanValue(database);
|
||||
}
|
||||
} else if (value instanceof DatabaseFunction) {
|
||||
return value.toString();
|
||||
} else if (value instanceof Boolean) {
|
||||
if (((Boolean) value)) {
|
||||
returnValue = this.getTrueBooleanValue(database);
|
||||
} else {
|
||||
returnValue = this.getFalseBooleanValue(database);
|
||||
}
|
||||
} else {
|
||||
throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value");
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
protected boolean isNumericBoolean(Database database) {
|
||||
if (database instanceof Firebird3Database) {
|
||||
return false;
|
||||
}
|
||||
if (database instanceof DerbyDatabase) {
|
||||
return !((DerbyDatabase) database).supportsBooleanDataType();
|
||||
} else if (database instanceof DB2Database) {
|
||||
return !((DB2Database) database).supportsBooleanDataType();
|
||||
}
|
||||
return (database instanceof Db2zDatabase)
|
||||
|| (database instanceof FirebirdDatabase)
|
||||
|| (database instanceof MSSQLDatabase)
|
||||
|| (database instanceof MySQLDatabase)
|
||||
|| (database instanceof OracleDatabase)
|
||||
|| (database instanceof SQLiteDatabase)
|
||||
|| (database instanceof SybaseASADatabase)
|
||||
|| (database instanceof SybaseDatabase)
|
||||
|| (database instanceof DmDatabase); // dhb52: DM Support
|
||||
}
|
||||
|
||||
/**
|
||||
* The database-specific value to use for "false" "boolean" columns.
|
||||
*/
|
||||
public String getFalseBooleanValue(Database database) {
|
||||
if (isNumericBoolean(database)) {
|
||||
return "0";
|
||||
}
|
||||
if (database instanceof InformixDatabase) {
|
||||
return "'f'";
|
||||
}
|
||||
return "FALSE";
|
||||
}
|
||||
|
||||
/**
|
||||
* The database-specific value to use for "true" "boolean" columns.
|
||||
*/
|
||||
public String getTrueBooleanValue(Database database) {
|
||||
if (isNumericBoolean(database)) {
|
||||
return "1";
|
||||
}
|
||||
if (database instanceof InformixDatabase) {
|
||||
return "'t'";
|
||||
}
|
||||
return "TRUE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadDataChange.LOAD_DATA_TYPE getLoadTypeName() {
|
||||
return LoadDataChange.LOAD_DATA_TYPE.BOOLEAN;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
防止IDEA将`.`和`/`混为一谈
|
|
@ -0,0 +1,21 @@
|
|||
liquibase.database.core.CockroachDatabase
|
||||
liquibase.database.core.DB2Database
|
||||
liquibase.database.core.Db2zDatabase
|
||||
liquibase.database.core.DerbyDatabase
|
||||
liquibase.database.core.Firebird3Database
|
||||
liquibase.database.core.FirebirdDatabase
|
||||
liquibase.database.core.H2Database
|
||||
liquibase.database.core.HsqlDatabase
|
||||
liquibase.database.core.InformixDatabase
|
||||
liquibase.database.core.Ingres9Database
|
||||
liquibase.database.core.MSSQLDatabase
|
||||
liquibase.database.core.MariaDBDatabase
|
||||
liquibase.database.core.MockDatabase
|
||||
liquibase.database.core.MySQLDatabase
|
||||
liquibase.database.core.OracleDatabase
|
||||
liquibase.database.core.PostgresDatabase
|
||||
liquibase.database.core.SQLiteDatabase
|
||||
liquibase.database.core.SybaseASADatabase
|
||||
liquibase.database.core.SybaseDatabase
|
||||
liquibase.database.core.DmDatabase
|
||||
liquibase.database.core.UnsupportedDatabase
|
|
@ -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
Load Diff
|
@ -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
Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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
Load Diff
|
@ -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
Load Diff
|
@ -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
Load Diff
|
@ -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
Load Diff
|
@ -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 = """/*
|
||||
Yudao 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: "Yudao@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: Yudao@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,675 @@
|
|||
<?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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-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.4.1-jdk8-SNAPSHOT</revision>
|
||||
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.framework.version>5.3.39</spring.framework.version>
|
||||
<spring.security.version>5.8.14</spring.security.version>
|
||||
<spring.boot.version>2.7.18</spring.boot.version>
|
||||
<!-- Web 相关 -->
|
||||
<springdoc.version>1.7.0</springdoc.version>
|
||||
<knife4j.version>4.5.0</knife4j.version>
|
||||
<servlet.versoin>2.5</servlet.versoin>
|
||||
<!-- DB 相关 -->
|
||||
<druid.version>1.2.24</druid.version>
|
||||
<mybatis.version>3.5.17</mybatis.version>
|
||||
<mybatis-plus.version>3.5.9</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.41.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>8.12.0</skywalking.version>
|
||||
<spring-boot-admin.version>2.7.15</spring-boot-admin.version>
|
||||
<opentracing.version>0.33.0</opentracing.version>
|
||||
<!-- Test 测试相关 -->
|
||||
<podam.version>7.2.11.RELEASE</podam.version> <!-- Spring Boot 2.X 最多使用 7.2.11 版本 -->
|
||||
<jedis-mock.version>1.1.8</jedis-mock.version>
|
||||
<mockito-inline.version>4.11.0</mockito-inline.version>
|
||||
<!-- Bpm 工作流相关 -->
|
||||
<flowable.version>6.8.0</flowable.version>
|
||||
<!-- 工具类相关 -->
|
||||
<captcha-plus.version>1.0.10</captcha-plus.version>
|
||||
<jsoup.version>1.18.3</jsoup.version>
|
||||
<lombok.version>1.18.36</lombok.version>
|
||||
<mapstruct.version>1.6.3</mapstruct.version>
|
||||
<hutool.version>5.8.35</hutool.version>
|
||||
<easyexcel.verion>4.0.3</easyexcel.verion>
|
||||
<velocity.version>2.4</velocity.version> <!-- JDK8 不能从 2.4 升级到 2.4.1,会报包不存在!!!! -->
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<guava.version>33.4.0-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.116.Final</netty.version>
|
||||
<mqtt.version>1.2.5</mqtt.version>
|
||||
<!-- 三方云服务相关 -->
|
||||
<commons-io.version>2.17.0</commons-io.version>
|
||||
<commons-compress.version>1.27.1</commons-compress.version>
|
||||
<aws-java-sdk-s3.version>1.12.777</aws-java-sdk-s3.version>
|
||||
<justauth.version>1.0.8</justauth.version>
|
||||
<jimureport.version>1.7.8</jimureport.version>
|
||||
<weixin-java.version>4.6.0</weixin-java.version>
|
||||
<!-- 专属于 JDK8 安全漏洞升级 -->
|
||||
<logback.version>1.2.13</logback.version> <!-- 无法使用 1.3.X 版本,启动会报错 -->
|
||||
</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</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId> <!-- JDK8 版本独有:保证 Spring Framework 尽量高 -->
|
||||
<version>${spring.framework.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-bom</artifactId> <!-- JDK8 版本独有:保证 Spring Security 尽量高 -->
|
||||
<version>${spring.security.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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-security</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-websocket</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-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-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-jsqlparser-4.9</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-boot-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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-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>
|
||||
<exclusion>
|
||||
<groupId>org.redisson</groupId>
|
||||
<!-- 使用 redisson-spring-data-27 替代,解决 Tuple NoClassDefFoundError 报错 -->
|
||||
<artifactId>redisson-spring-data-34</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-data-27</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
</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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-job</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-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>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
<version>${revision}</version>
|
||||
<scope>test</scope>
|
||||
</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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-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.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.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<version>${guice.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.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-s3</artifactId>
|
||||
<version>${aws-java-sdk-s3.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-boot-starter</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>
|
||||
|
||||
<!-- 专属于 JDK8 安全漏洞升级 -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.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,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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>yudao</artifactId>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>yudao-common</module>
|
||||
<module>yudao-spring-boot-starter-mybatis</module>
|
||||
<module>yudao-spring-boot-starter-redis</module>
|
||||
<module>yudao-spring-boot-starter-web</module>
|
||||
<module>yudao-spring-boot-starter-security</module>
|
||||
<module>yudao-spring-boot-starter-websocket</module>
|
||||
|
||||
<module>yudao-spring-boot-starter-monitor</module>
|
||||
<module>yudao-spring-boot-starter-protection</module>
|
||||
<module>yudao-spring-boot-starter-job</module>
|
||||
<module>yudao-spring-boot-starter-mq</module>
|
||||
|
||||
<module>yudao-spring-boot-starter-excel</module>
|
||||
<module>yudao-spring-boot-starter-test</module>
|
||||
|
||||
<module>yudao-spring-boot-starter-biz-tenant</module>
|
||||
<module>yudao-spring-boot-starter-biz-data-permission</module>
|
||||
<module>yudao-spring-boot-starter-biz-ip</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>yudao-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>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-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-ui</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 cn.iocoder.yudao.framework.common.core;
|
||||
|
||||
/**
|
||||
* 可生成 T 数组的接口
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public interface ArrayValuable<T> {
|
||||
|
||||
/**
|
||||
* @return 数组
|
||||
*/
|
||||
T[] array();
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.enums;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 通用状态枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CommonStatusEnum implements ArrayValuable<Integer> {
|
||||
|
||||
ENABLE(0, "开启"),
|
||||
DISABLE(1, "关闭");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 状态值
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] 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 cn.iocoder.yudao.framework.common.enums;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 时间间隔的枚举
|
||||
*
|
||||
* @author dhb52
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DateIntervalEnum implements ArrayValuable<Integer> {
|
||||
|
||||
DAY(1, "天"),
|
||||
WEEK(2, "周"),
|
||||
MONTH(3, "月"),
|
||||
QUARTER(4, "季度"),
|
||||
YEAR(5, "年")
|
||||
;
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DateIntervalEnum::getInterval).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer interval;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static DateIntervalEnum valueOf(Integer interval) {
|
||||
return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.enums;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 终端的枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum TerminalEnum implements ArrayValuable<Integer> {
|
||||
|
||||
UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
|
||||
WECHAT_MINI_PROGRAM(10, "微信小程序"),
|
||||
WECHAT_WAP(11, "微信公众号"),
|
||||
H5(20, "H5 网页"),
|
||||
APP(31, "手机 App"),
|
||||
;
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 终端
|
||||
*/
|
||||
private final Integer terminal;
|
||||
/**
|
||||
* 终端名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package cn.iocoder.yudao.framework.common.enums;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 全局用户类型枚举
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum UserTypeEnum implements ArrayValuable<Integer> {
|
||||
|
||||
MEMBER(1, "会员"), // 面向 c 端,普通用户
|
||||
ADMIN(2, "管理员"); // 面向 b 端,管理后台
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
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 Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.exception;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.exception;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 服务器异常 Exception
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public final class ServerException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* 全局错误码
|
||||
*
|
||||
* @see GlobalErrorCodeConstants
|
||||
*/
|
||||
private Integer code;
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 空构造方法,避免反序列化问题
|
||||
*/
|
||||
public ServerException() {
|
||||
}
|
||||
|
||||
public ServerException(ErrorCode errorCode) {
|
||||
this.code = errorCode.getCode();
|
||||
this.message = errorCode.getMsg();
|
||||
}
|
||||
|
||||
public ServerException(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public ServerException setCode(Integer code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public ServerException setMessage(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package cn.iocoder.yudao.framework.common.exception;
|
||||
|
||||
import cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.exception.enums;
|
||||
|
||||
import cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.exception.util;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common;
|
|
@ -0,0 +1,121 @@
|
|||
package cn.iocoder.yudao.framework.common.pojo;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
|
||||
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.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), code, "code 必须是错误的!");
|
||||
CommonResult<T> result = new CommonResult<>();
|
||||
result.code = code;
|
||||
result.msg = message;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> CommonResult<T> error(ErrorCode errorCode, Object... params) {
|
||||
Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), errorCode.getCode(), "code 必须是错误的!");
|
||||
CommonResult<T> result = new CommonResult<>();
|
||||
result.code = errorCode.getCode();
|
||||
result.msg = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), params);
|
||||
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 cn.iocoder.yudao.framework.common.pojo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.util.cache;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Cache 工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class CacheUtils {
|
||||
|
||||
/**
|
||||
* 构建异步刷新的 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 cn.iocoder.yudao.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 cn.iocoder.yudao.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,338 @@
|
|||
package cn.iocoder.yudao.framework.common.util.collection;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
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> PageResult<U> convertPage(PageResult<T> from, Function<T, U> func) {
|
||||
if (ArrayUtil.isEmpty(from)) {
|
||||
return new PageResult<>(from.getTotal());
|
||||
}
|
||||
return new PageResult<>(convertList(from.getList(), func), from.getTotal());
|
||||
}
|
||||
|
||||
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().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package cn.iocoder.yudao.framework.common.util.collection;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 javax.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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,210 @@
|
|||
package cn.iocoder.yudao.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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为 JSON 类型的字符串
|
||||
* @param str 字符串
|
||||
*/
|
||||
public static boolean isJsonObject(String str) {
|
||||
return JSONUtil.isTypeJSONObject(str);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.util.object;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortingField;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* {@link cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.util;
|
|
@ -0,0 +1,119 @@
|
|||
package cn.iocoder.yudao.framework.common.util.servlet;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
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 javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
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);
|
||||
ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回附件
|
||||
*
|
||||
* @param response 响应
|
||||
* @param filename 文件名
|
||||
* @param content 附件内容
|
||||
*/
|
||||
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
|
||||
// 设置 header 和 contentType
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
// 输出附件
|
||||
IoUtil.write(response.getOutputStream(), false, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 ServletUtil.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 ServletUtil.getBody(request);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] getBodyBytes(HttpServletRequest request) {
|
||||
// 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取
|
||||
if (isJsonRequest(request)) {
|
||||
return ServletUtil.getBodyBytes(request);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getClientIP(HttpServletRequest request) {
|
||||
return ServletUtil.getClientIP(request);
|
||||
}
|
||||
|
||||
public static Map<String, String> getParamMap(HttpServletRequest request) {
|
||||
return ServletUtil.getParamMap(request);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.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 cn.iocoder.yudao.framework.common.util.validation;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Validation;
|
||||
import javax.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 cn.iocoder.yudao.framework.common.validation;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.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 实现 ArrayValuable 接口的类
|
||||
*/
|
||||
Class<? extends ArrayValuable<?>> value();
|
||||
|
||||
String message() default "必须在指定范围 {value}";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package cn.iocoder.yudao.framework.common.validation;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {
|
||||
|
||||
private List<?> values;
|
||||
|
||||
@Override
|
||||
public void initialize(InEnum annotation) {
|
||||
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
|
||||
if (values.length == 0) {
|
||||
this.values = Collections.emptyList();
|
||||
} else {
|
||||
this.values = Arrays.asList(values[0].array());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {
|
||||
if (list == null) {
|
||||
return true;
|
||||
}
|
||||
// 校验通过
|
||||
if (CollUtil.containsAll(values, list)) {
|
||||
return true;
|
||||
}
|
||||
// 校验不通过,自定义提示语句
|
||||
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
|
||||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
|
||||
.replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package cn.iocoder.yudao.framework.common.validation;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
|
||||
|
||||
private List<?> values;
|
||||
|
||||
@Override
|
||||
public void initialize(InEnum annotation) {
|
||||
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
|
||||
if (values.length == 0) {
|
||||
this.values = Collections.emptyList();
|
||||
} else {
|
||||
this.values = Arrays.asList(values[0].array());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
||||
// 为空时,默认不校验,即认为通过
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
// 校验通过
|
||||
if (values.contains(value)) {
|
||||
return true;
|
||||
}
|
||||
// 校验不通过,自定义提示语句
|
||||
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
|
||||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
|
||||
.replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue