Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk21-ai
# Conflicts: # yudao-dependencies/pom.xml # yudao-server/src/main/resources/application-local.yaml
This commit is contained in:
commit
16a47bde5b
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
60
README.md
60
README.md
|
@ -21,6 +21,18 @@
|
||||||
* 启动文档:<https://doc.iocoder.cn/quick-start/>
|
* 启动文档:<https://doc.iocoder.cn/quick-start/>
|
||||||
* 视频教程:<https://doc.iocoder.cn/video/>
|
* 视频教程:<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% 免费使用。
|
**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
|
||||||
|
@ -31,7 +43,7 @@
|
||||||
|
|
||||||
![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
|
![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
|
||||||
|
|
||||||
* Java 后端:`master` 分支为 JDK 8 + Spring Boot 2.7.18,`master-jdk21` 分支为 JDK21 + Spring Boot 3.2.0
|
* 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` 版本
|
* 管理后台的电脑端:Vue3 提供 `element-plus`、`vben(ant-design-vue)` 两个版本,Vue2 提供 `element-ui` 版本
|
||||||
* 管理后台的移动端:采用 `uni-app` 方案,一份代码多终端适配,同时支持 APP、小程序、H5!
|
* 管理后台的移动端:采用 `uni-app` 方案,一份代码多终端适配,同时支持 APP、小程序、H5!
|
||||||
* 后端采用 Spring Boot 多模块架构、MySQL + MyBatis Plus、Redis + Redisson
|
* 后端采用 Spring Boot 多模块架构、MySQL + MyBatis Plus、Redis + Redisson
|
||||||
|
@ -72,28 +84,6 @@
|
||||||
| [yudao-ui-admin-uniapp](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-admin-uniapp/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-admin-uniapp.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-admin-uniapp) | 基于 Vue2 + element-ui 实现的管理后台 |
|
| [yudao-ui-admin-uniapp](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-admin-uniapp/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-admin-uniapp.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-admin-uniapp) | 基于 Vue2 + element-ui 实现的管理后台 |
|
||||||
| [yudao-ui-go-view](https://gitee.com/yudaocode/yudao-ui-go-view) | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-go-view/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-go-view) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-go-view.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-go-view) | 基于 Vue3 + naive-ui 实现的大屏报表 |
|
| [yudao-ui-go-view](https://gitee.com/yudaocode/yudao-ui-go-view) | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-go-view/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-go-view) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-go-view.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-go-view) | 基于 Vue3 + naive-ui 实现的大屏报表 |
|
||||||
|
|
||||||
## 🐰 分支说明
|
|
||||||
|
|
||||||
### ⬅️ 完整版
|
|
||||||
|
|
||||||
【完整版】包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM 等功能
|
|
||||||
|
|
||||||
* JDK 8 + Spring Boot 2.7.18 版本:<https://gitee.com/zhijiantianya/ruoyi-vue-pro> 的 `master` 分支
|
|
||||||
* JDK 21 + Spring Boot 3.2.0 版本:<https://gitee.com/zhijiantianya/ruoyi-vue-pro> 的 `master-jdk21` 分支
|
|
||||||
|
|
||||||
两个分支的功能是一致的,可以放心使用!
|
|
||||||
|
|
||||||
### ➡️️ 精简版
|
|
||||||
|
|
||||||
【精简版】只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM 等功能
|
|
||||||
|
|
||||||
* JDK 8 + Spring Boot 2.7.18 版本:<https://gitee.com/yudaocode/yudao-boot-mini> 的 `master` 分支
|
|
||||||
* JDK 21 + Spring Boot 3.2.0 版本:<https://gitee.com/yudaocode/yudao-boot-mini> 的 `master-jdk21` 分支
|
|
||||||
|
|
||||||
如果你想把【完整版】的功能,迁移到【精简版】,可以参考 [《迁移功能到精简版》](https://doc.iocoder.cn/migrate-module/) 文档。
|
|
||||||
|
|
||||||
如果你想把【完整版】的功能,迁移到【精简版】,可以参考 [《迁移功能到精简版》](https://doc.iocoder.cn/migrate-module/) 文档。
|
|
||||||
|
|
||||||
## 😎 开源协议
|
## 😎 开源协议
|
||||||
|
|
||||||
**为什么推荐使用本项目?**
|
**为什么推荐使用本项目?**
|
||||||
|
@ -120,16 +110,9 @@
|
||||||
|
|
||||||
![功能分层](/.image/common/ruoyi-vue-pro-biz.png)
|
![功能分层](/.image/common/ruoyi-vue-pro-biz.png)
|
||||||
|
|
||||||
* 系统功能
|
* 通用模块(必选):系统功能、基础设施
|
||||||
* 基础设施
|
* 通用模块(可选):工作流程、支付系统、数据报表、会员中心
|
||||||
* 工作流程
|
* 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号
|
||||||
* 支付系统
|
|
||||||
* 会员中心
|
|
||||||
* 数据报表
|
|
||||||
* 商城系统
|
|
||||||
* 微信公众号
|
|
||||||
* ERP 系统
|
|
||||||
* CRM 系统
|
|
||||||
|
|
||||||
> 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
|
> 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
|
||||||
>
|
>
|
||||||
|
@ -162,6 +145,8 @@
|
||||||
| 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
|
| 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
|
||||||
| 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 |
|
| 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 |
|
||||||
|
|
||||||
|
![功能图](/.image/common/system-feature.png)
|
||||||
|
|
||||||
### 工作流程
|
### 工作流程
|
||||||
|
|
||||||
| | 功能 | 描述 |
|
| | 功能 | 描述 |
|
||||||
|
@ -174,6 +159,8 @@
|
||||||
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 |
|
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 |
|
||||||
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
|
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
|
||||||
|
|
||||||
|
![功能图](/.image/common/bpm-feature.png)
|
||||||
|
|
||||||
### 支付系统
|
### 支付系统
|
||||||
|
|
||||||
| | 功能 | 描述 |
|
| | 功能 | 描述 |
|
||||||
|
@ -203,12 +190,12 @@
|
||||||
| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
|
| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
|
||||||
| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 |
|
| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 |
|
||||||
| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 |
|
| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 |
|
||||||
| 🚀 | 分布式锁 | 基于 Redis 实现分布式锁,满足并发场景 |
|
| 🚀 | 服务保障 | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景 |
|
||||||
| 🚀 | 幂等组件 | 基于 Redis 实现幂等组件,解决重复请求问题 |
|
|
||||||
| 🚀 | 服务保障 | 基于 Resilience4j 实现服务的稳定性,包括限流、熔断等功能 |
|
|
||||||
| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 |
|
| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 |
|
||||||
| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 |
|
| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 |
|
||||||
|
|
||||||
|
![功能图](/.image/common/infra-feature.png)
|
||||||
|
|
||||||
### 数据报表
|
### 数据报表
|
||||||
|
|
||||||
| | 功能 | 描述 |
|
| | 功能 | 描述 |
|
||||||
|
@ -298,7 +285,6 @@
|
||||||
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](https://doc.iocoder.cn/bpm/) |
|
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](https://doc.iocoder.cn/bpm/) |
|
||||||
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
|
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
|
||||||
| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.2.0 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
|
| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.2.0 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
|
||||||
| [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 2.1.0 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) |
|
|
||||||
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.0.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
|
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.0.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
|
||||||
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.1.8 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
|
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.1.8 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
|
||||||
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.15.3 | |
|
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.15.3 | |
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -24,8 +24,6 @@
|
||||||
<!-- <module>yudao-module-mall</module>-->
|
<!-- <module>yudao-module-mall</module>-->
|
||||||
<!-- <module>yudao-module-crm</module>-->
|
<!-- <module>yudao-module-crm</module>-->
|
||||||
<!-- <module>yudao-module-erp</module>-->
|
<!-- <module>yudao-module-erp</module>-->
|
||||||
<!-- 示例项目 -->
|
|
||||||
<!-- <module>yudao-example</module>-->
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<name>${project.artifactId}</name>
|
<name>${project.artifactId}</name>
|
||||||
|
@ -33,9 +31,9 @@
|
||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>2.0.1-snapshot</revision>
|
<revision>2.1.0-snapshot</revision>
|
||||||
<!-- Maven 相关 -->
|
<!-- Maven 相关 -->
|
||||||
<java.version>21</java.version>
|
<java.version>17</java.version>
|
||||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
<maven-surefire-plugin.version>3.2.2</maven-surefire-plugin.version>
|
<maven-surefire-plugin.version>3.2.2</maven-surefire-plugin.version>
|
||||||
|
|
|
@ -50,7 +50,7 @@ services:
|
||||||
--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.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.username=${SLAVE_DATASOURCE_USERNAME:-root}
|
||||||
--spring.datasource.dynamic.datasource.slave.password=${SLAVE_DATASOURCE_PASSWORD:-123456}
|
--spring.datasource.dynamic.datasource.slave.password=${SLAVE_DATASOURCE_PASSWORD:-123456}
|
||||||
--spring.redis.host=${REDIS_HOST:-yudao-redis}
|
--spring.data.redis.host=${REDIS_HOST:-yudao-redis}
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
- redis
|
- redis
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,77 +0,0 @@
|
||||||
-- 菜单 SQL
|
|
||||||
INSERT INTO system_menu(
|
|
||||||
name, permission, type, sort, parent_id,
|
|
||||||
path, icon, component, status, component_name
|
|
||||||
)
|
|
||||||
VALUES (
|
|
||||||
'Ureport2报表管理', '', 2, 0, 1281,
|
|
||||||
'ureport-data', '', 'report/ureport/index', 0, 'UReportData'
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 按钮父菜单ID
|
|
||||||
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
|
|
||||||
SELECT @parentId := LAST_INSERT_ID();
|
|
||||||
|
|
||||||
-- 按钮 SQL
|
|
||||||
INSERT INTO system_menu(
|
|
||||||
name, permission, type, sort, parent_id,
|
|
||||||
path, icon, component, status
|
|
||||||
)
|
|
||||||
VALUES (
|
|
||||||
'Ureport2报表查询', 'report:ureport-data:query', 3, 1, @parentId,
|
|
||||||
'', '', '', 0
|
|
||||||
);
|
|
||||||
INSERT INTO system_menu(
|
|
||||||
name, permission, type, sort, parent_id,
|
|
||||||
path, icon, component, status
|
|
||||||
)
|
|
||||||
VALUES (
|
|
||||||
'Ureport2报表创建', 'report:ureport-data:create', 3, 2, @parentId,
|
|
||||||
'', '', '', 0
|
|
||||||
);
|
|
||||||
INSERT INTO system_menu(
|
|
||||||
name, permission, type, sort, parent_id,
|
|
||||||
path, icon, component, status
|
|
||||||
)
|
|
||||||
VALUES (
|
|
||||||
'Ureport2报表更新', 'report:ureport-data:update', 3, 3, @parentId,
|
|
||||||
'', '', '', 0
|
|
||||||
);
|
|
||||||
INSERT INTO system_menu(
|
|
||||||
name, permission, type, sort, parent_id,
|
|
||||||
path, icon, component, status
|
|
||||||
)
|
|
||||||
VALUES (
|
|
||||||
'Ureport2报表删除', 'report:ureport-data:delete', 3, 4, @parentId,
|
|
||||||
'', '', '', 0
|
|
||||||
);
|
|
||||||
INSERT INTO system_menu(
|
|
||||||
name, permission, type, sort, parent_id,
|
|
||||||
path, icon, component, status
|
|
||||||
)
|
|
||||||
VALUES (
|
|
||||||
'Ureport2报表导出', 'report:ureport-data:export', 3, 5, @parentId,
|
|
||||||
'', '', '', 0
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `report_ureport_data`;
|
|
||||||
CREATE TABLE `report_ureport_data` (
|
|
||||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
|
||||||
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件名称',
|
|
||||||
`status` tinyint(4) NOT NULL COMMENT '状态',
|
|
||||||
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '文件内容',
|
|
||||||
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
|
|
||||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
|
||||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|
||||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
|
|
||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
|
||||||
`tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户编号',
|
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Ureport2报表' ROW_FORMAT = Dynamic;
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- Records of report_ureport_data
|
|
||||||
-- ----------------------------
|
|
||||||
INSERT INTO `report_ureport_data` VALUES (11, 'role.ureport.xml', 0, '<?xml version=\"1.0\" encoding=\"UTF-8\"?><ureport><cell expand=\"Down\" name=\"A1\" row=\"1\" col=\"1\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><dataset-value dataset-name=\"role\" aggregate=\"group\" property=\"name\" order=\"none\" mapping-type=\"simple\"></dataset-value></cell><cell expand=\"Down\" name=\"B1\" row=\"1\" col=\"2\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><dataset-value dataset-name=\"role\" aggregate=\"group\" property=\"code\" order=\"none\" mapping-type=\"simple\"></dataset-value></cell><cell expand=\"Down\" name=\"C1\" row=\"1\" col=\"3\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><dataset-value dataset-name=\"role\" aggregate=\"group\" property=\"status\" order=\"none\" mapping-type=\"simple\"></dataset-value></cell><cell expand=\"None\" name=\"D1\" row=\"1\" col=\"4\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><simple-value><![CDATA[]]></simple-value></cell><cell expand=\"None\" name=\"A2\" row=\"2\" col=\"1\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><simple-value><![CDATA[]]></simple-value></cell><cell expand=\"None\" name=\"B2\" row=\"2\" col=\"2\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><simple-value><![CDATA[]]></simple-value></cell><cell expand=\"None\" name=\"C2\" row=\"2\" col=\"3\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><simple-value><![CDATA[]]></simple-value></cell><cell expand=\"None\" name=\"D2\" row=\"2\" col=\"4\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><simple-value><![CDATA[]]></simple-value></cell><cell expand=\"None\" name=\"A3\" row=\"3\" col=\"1\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><simple-value><![CDATA[]]></simple-value></cell><cell expand=\"None\" name=\"B3\" row=\"3\" col=\"2\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><simple-value><![CDATA[]]></simple-value></cell><cell expand=\"None\" name=\"C3\" row=\"3\" col=\"3\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><simple-value><![CDATA[]]></simple-value></cell><cell expand=\"None\" name=\"D3\" row=\"3\" col=\"4\"><cell-style font-size=\"10\" align=\"center\" valign=\"middle\"></cell-style><simple-value><![CDATA[]]></simple-value></cell><row row-number=\"1\" height=\"18\"/><row row-number=\"2\" height=\"18\"/><row row-number=\"3\" height=\"18\"/><column col-number=\"1\" width=\"80\"/><column col-number=\"2\" width=\"80\"/><column col-number=\"3\" width=\"80\"/><column col-number=\"4\" width=\"80\"/><datasource name=\"UReportDataSource\" type=\"buildin\"><dataset name=\"role\" type=\"sql\"><sql><![CDATA[select * from system_role]]></sql><field name=\"id\"/><field name=\"name\"/><field name=\"code\"/><field name=\"sort\"/><field name=\"data_scope\"/><field name=\"data_scope_dept_ids\"/><field name=\"status\"/><field name=\"type\"/><field name=\"remark\"/><field name=\"creator\"/><field name=\"create_time\"/><field name=\"updater\"/><field name=\"update_time\"/><field name=\"deleted\"/><field name=\"tenant_id\"/></dataset></datasource><paper type=\"A4\" left-margin=\"90\" right-margin=\"90\"\n top-margin=\"72\" bottom-margin=\"72\" paging-mode=\"fitpage\" fixrows=\"0\"\n width=\"595\" height=\"842\" orientation=\"portrait\" html-report-align=\"left\" bg-image=\"\" html-interval-refresh-value=\"0\" column-enabled=\"false\"></paper></ureport>', NULL, NULL, '2023-11-25 22:40:58', NULL, '2023-11-25 23:00:42', b'0', 0);
|
|
|
@ -0,0 +1,305 @@
|
||||||
|
/*
|
||||||
|
注意:仅仅需要 Quartz 定时任务的场景,可选!!!
|
||||||
|
|
||||||
|
Date: 30/04/2024 09:54:18
|
||||||
|
*/
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_BLOB_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`;
|
||||||
|
CREATE TABLE `QRTZ_BLOB_TRIGGERS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`BLOB_DATA` blob NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
|
||||||
|
INDEX `SCHED_NAME`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE,
|
||||||
|
CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_BLOB_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_CALENDARS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_CALENDARS`;
|
||||||
|
CREATE TABLE `QRTZ_CALENDARS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`CALENDAR_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`CALENDAR` blob NOT NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `CALENDAR_NAME`) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_CALENDARS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_CRON_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`;
|
||||||
|
CREATE TABLE `QRTZ_CRON_TRIGGERS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`CRON_EXPRESSION` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TIME_ZONE_ID` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
|
||||||
|
CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_CRON_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'accessLogCleanJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'brokerageRecordUnfreezeJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'errorLogCleanJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'jobLogCleanJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', '* * * * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payRefundSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'tradeOrderAutoCancelJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'tradeOrderAutoCommentJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai');
|
||||||
|
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'tradeOrderAutoReceiveJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai');
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_FIRED_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`;
|
||||||
|
CREATE TABLE `QRTZ_FIRED_TRIGGERS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`ENTRY_ID` varchar(95) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`INSTANCE_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`FIRED_TIME` bigint NOT NULL,
|
||||||
|
`SCHED_TIME` bigint NOT NULL,
|
||||||
|
`PRIORITY` int NOT NULL,
|
||||||
|
`STATE` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`JOB_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`JOB_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `ENTRY_ID`) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_FT_TRIG_INST_NAME`(`SCHED_NAME` ASC, `INSTANCE_NAME` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY`(`SCHED_NAME` ASC, `INSTANCE_NAME` ASC, `REQUESTS_RECOVERY` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_FT_J_G`(`SCHED_NAME` ASC, `JOB_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_FT_JG`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_FT_T_G`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_FT_TG`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_FIRED_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_JOB_DETAILS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`;
|
||||||
|
CREATE TABLE `QRTZ_JOB_DETAILS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`JOB_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`JOB_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`DESCRIPTION` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`JOB_CLASS_NAME` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`IS_DURABLE` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`IS_UPDATE_DATA` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`JOB_DATA` blob NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_J_REQ_RECOVERY`(`SCHED_NAME` ASC, `REQUESTS_RECOVERY` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_J_GRP`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_JOB_DETAILS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'accessLogCleanJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000197400104A4F425F48414E444C45525F4E414D457400116163636573734C6F67436C65616E4A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'brokerageRecordUnfreezeJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000187400104A4F425F48414E444C45525F4E414D4574001A62726F6B65726167655265636F7264556E667265657A654A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'errorLogCleanJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000000000000001A7400104A4F425F48414E444C45525F4E414D457400106572726F724C6F67436C65616E4A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'jobLogCleanJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000000000000001B7400104A4F425F48414E444C45525F4E414D4574000E6A6F624C6F67436C65616E4A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000057400104A4F425F48414E444C45525F4E414D4574000C7061794E6F746966794A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000127400104A4F425F48414E444C45525F4E414D457400117061794F726465724578706972654A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000117400104A4F425F48414E444C45525F4E414D4574000F7061794F7264657253796E634A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payRefundSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000137400104A4F425F48414E444C45525F4E414D45740010706179526566756E6453796E634A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoCancelJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000157400104A4F425F48414E444C45525F4E414D4574001774726164654F726465724175746F43616E63656C4A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoCommentJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000177400104A4F425F48414E444C45525F4E414D4574001874726164654F726465724175746F436F6D6D656E744A6F627800);
|
||||||
|
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoReceiveJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000167400104A4F425F48414E444C45525F4E414D4574001874726164654F726465724175746F526563656976654A6F627800);
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_LOCKS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_LOCKS`;
|
||||||
|
CREATE TABLE `QRTZ_LOCKS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`LOCK_NAME` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `LOCK_NAME`) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_LOCKS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO `QRTZ_LOCKS` (`SCHED_NAME`, `LOCK_NAME`) VALUES ('schedulerName', 'STATE_ACCESS');
|
||||||
|
INSERT INTO `QRTZ_LOCKS` (`SCHED_NAME`, `LOCK_NAME`) VALUES ('schedulerName', 'TRIGGER_ACCESS');
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_PAUSED_TRIGGER_GRPS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`;
|
||||||
|
CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_GROUP`) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_PAUSED_TRIGGER_GRPS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_SCHEDULER_STATE
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;
|
||||||
|
CREATE TABLE `QRTZ_SCHEDULER_STATE` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`INSTANCE_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`LAST_CHECKIN_TIME` bigint NOT NULL,
|
||||||
|
`CHECKIN_INTERVAL` bigint NOT NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `INSTANCE_NAME`) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_SCHEDULER_STATE
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('schedulerName', 'MacBook-Pro.local1713489703551', 1713742509534, 15000);
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_SIMPLE_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`;
|
||||||
|
CREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci 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`) USING BTREE,
|
||||||
|
CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_SIMPLE_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_SIMPROP_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`;
|
||||||
|
CREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`STR_PROP_1` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`STR_PROP_2` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`STR_PROP_3` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`INT_PROP_1` int NULL DEFAULT NULL,
|
||||||
|
`INT_PROP_2` int NULL DEFAULT NULL,
|
||||||
|
`LONG_PROP_1` bigint NULL DEFAULT NULL,
|
||||||
|
`LONG_PROP_2` bigint NULL DEFAULT NULL,
|
||||||
|
`DEC_PROP_1` decimal(13, 4) NULL DEFAULT NULL,
|
||||||
|
`DEC_PROP_2` decimal(13, 4) NULL DEFAULT NULL,
|
||||||
|
`BOOL_PROP_1` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`BOOL_PROP_2` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
|
||||||
|
CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_SIMPROP_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for QRTZ_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `QRTZ_TRIGGERS`;
|
||||||
|
CREATE TABLE `QRTZ_TRIGGERS` (
|
||||||
|
`SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`JOB_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`JOB_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`DESCRIPTION` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`NEXT_FIRE_TIME` bigint NULL DEFAULT NULL,
|
||||||
|
`PREV_FIRE_TIME` bigint NULL DEFAULT NULL,
|
||||||
|
`PRIORITY` int NULL DEFAULT NULL,
|
||||||
|
`TRIGGER_STATE` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`TRIGGER_TYPE` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
`START_TIME` bigint NOT NULL,
|
||||||
|
`END_TIME` bigint NULL DEFAULT NULL,
|
||||||
|
`CALENDAR_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||||
|
`MISFIRE_INSTR` smallint NULL DEFAULT NULL,
|
||||||
|
`JOB_DATA` blob NULL,
|
||||||
|
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_J`(`SCHED_NAME` ASC, `JOB_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_JG`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_C`(`SCHED_NAME` ASC, `CALENDAR_NAME` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_G`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_STATE`(`SCHED_NAME` ASC, `TRIGGER_STATE` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_N_STATE`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_N_G_STATE`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_NEXT_FIRE_TIME`(`SCHED_NAME` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_NFT_ST`(`SCHED_NAME` ASC, `TRIGGER_STATE` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_NFT_MISFIRE`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC, `TRIGGER_STATE` ASC) USING BTREE,
|
||||||
|
INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE,
|
||||||
|
CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of QRTZ_TRIGGERS
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'accessLogCleanJob', 'DEFAULT', 'accessLogCleanJob', 'DEFAULT', NULL, 1696348800000, -1, 5, 'PAUSED', 'CRON', 1696301981000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'brokerageRecordUnfreezeJob', 'DEFAULT', 'brokerageRecordUnfreezeJob', 'DEFAULT', NULL, 1695909720000, -1, 5, 'PAUSED', 'CRON', 1695909706000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'errorLogCleanJob', 'DEFAULT', 'errorLogCleanJob', 'DEFAULT', NULL, 1696348800000, -1, 5, 'PAUSED', 'CRON', 1696302043000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'jobLogCleanJob', 'DEFAULT', 'jobLogCleanJob', 'DEFAULT', NULL, 1696348800000, -1, 5, 'PAUSED', 'CRON', 1696302092000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', 'payNotifyJob', 'DEFAULT', NULL, 1688907102000, 1688907101000, 5, 'PAUSED', 'CRON', 1635294882000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', 'payOrderExpireJob', 'DEFAULT', NULL, 1690011600000, -1, 5, 'PAUSED', 'CRON', 1690011553000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', 'payOrderSyncJob', 'DEFAULT', NULL, 1690011600000, 1690011540000, 5, 'PAUSED', 'CRON', 1690007785000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payRefundSyncJob', 'DEFAULT', 'payRefundSyncJob', 'DEFAULT', NULL, 1690117560000, 1690117500000, 5, 'PAUSED', 'CRON', 1690117424000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoCancelJob', 'DEFAULT', 'tradeOrderAutoCancelJob', 'DEFAULT', NULL, 1695727440000, 1695727380000, 5, 'PAUSED', 'CRON', 1695656605000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoCommentJob', 'DEFAULT', 'tradeOrderAutoCommentJob', 'DEFAULT', NULL, 1695783840000, 1695783780000, 5, 'PAUSED', 'CRON', 1695742709000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
|
||||||
|
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoReceiveJob', 'DEFAULT', 'tradeOrderAutoReceiveJob', 'DEFAULT', NULL, 1695742740000, 1695742680000, 5, 'PAUSED', 'CRON', 1695727433000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
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,101 @@
|
||||||
|
## 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
|
||||||
|
docker compose up -d oracle
|
||||||
|
```
|
||||||
|
|
||||||
|
暂不支持 MacBook Apple Silicon,因为 Oracle 官方没有提供 Apple Silicon 版本的 Docker 镜像。
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
暂不支持 MacBook Apple Silicon,因为 SQL Server 官方没有提供 Apple Silicon 版本的 Docker 镜像。
|
||||||
|
|
||||||
|
### 1.5 DM 达梦
|
||||||
|
|
||||||
|
① 下载达梦 Docker 镜像:https://download.dameng.com/eco/dm8/dm8_20230808_rev197096_x86_rh6_64_single.tar
|
||||||
|
|
||||||
|
② 加载镜像文件,在镜像 tar 文件所在目录运行:
|
||||||
|
|
||||||
|
```Bash
|
||||||
|
docker load -i dm8_20230808_rev197096_x86_rh6_64_single.tar
|
||||||
|
````
|
||||||
|
|
||||||
|
③ 在项目 `sql/tools` 目录下运行:
|
||||||
|
|
||||||
|
```Bash
|
||||||
|
docker compose up -d dm8
|
||||||
|
# 注意:启动完 dm 后,需要手动再执行如下命令,因为 dm 不支持初始化脚本
|
||||||
|
docker compose exec dm8 bash -c "exec /opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql"
|
||||||
|
exit
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**: `sql/dm/ruoyi-vue-pro-dm8.sql` 文件编码必须为 `GBK` 或者 `GBK` 超集,否则会出现中文乱码。
|
||||||
|
|
||||||
|
暂不支持 MacBook Apple Silicon,因为 达梦 官方没有提供 Apple Silicon 版本的 Docker 镜像。
|
||||||
|
|
||||||
|
## 1.X 容器的销毁重建
|
||||||
|
|
||||||
|
开发测试过程中,有时候需要创建全新干净的数据库。由于测试数据 Docker 容器采用数据卷 Volume 挂载数据库实例的数据目录,因此销毁数据需要停止容器后,删除数据卷,然后再重新创建容器。
|
||||||
|
|
||||||
|
以 postgres 为例,操作如下:
|
||||||
|
|
||||||
|
```Bash
|
||||||
|
docker compose down postgres
|
||||||
|
docker volume rm ruoyi-vue-pro_postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. MySQL 转换其它数据库
|
||||||
|
|
||||||
|
### 2.1 实现原理
|
||||||
|
|
||||||
|
通过读取 MySQL 的 `sql/mysql/ruoyi-vue-pro.sql` 数据库文件,转换成 Oracle、PostgreSQL、SQL Server 等数据库的脚本。
|
||||||
|
|
||||||
|
### 2.2 使用方法
|
||||||
|
|
||||||
|
① 安装依赖库 `simple-ddl-parser`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install simple-ddl-parser
|
||||||
|
# pip3 install simple-ddl-parser
|
||||||
|
```
|
||||||
|
|
||||||
|
② 执行如下命令打印生成 postgres 的脚本内容,其他可选参数有:`oracle`、`sqlserver`、`dm8`:
|
||||||
|
|
||||||
|
```Bash
|
||||||
|
python3 convertor.py postgres
|
||||||
|
# python3 convertor.py postgres > tmp.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
程序将 SQL 脚本打印到终端,可以重定向到临时文件 `tmp.sql`。
|
||||||
|
|
||||||
|
确认无误后,可以利用 IDEA 进行格式化。当然,也可以直接导入到数据库中。
|
|
@ -0,0 +1,781 @@
|
||||||
|
# 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
|
||||||
|
(
|
||||||
|
);"""
|
||||||
|
|
||||||
|
|
||||||
|
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};
|
||||||
|
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 NULL
|
||||||
|
)
|
||||||
|
GO
|
||||||
|
|
||||||
|
EXEC sp_addextendedproperty
|
||||||
|
'MS_Description', N'数据库连接的表',
|
||||||
|
'SCHEMA', N'dbo',
|
||||||
|
'TABLE', N'dual'
|
||||||
|
GO"""
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="芋道系统数据库转换工具")
|
||||||
|
parser.add_argument(
|
||||||
|
"type",
|
||||||
|
type=str,
|
||||||
|
help="目标数据库类型",
|
||||||
|
choices=["postgres", "oracle", "sqlserver", "dm8"],
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f"不支持目标数据库类型: {args.type}")
|
||||||
|
|
||||||
|
convertor.print()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -0,0 +1,94 @@
|
||||||
|
name: ruoyi-vue-pro
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql: { }
|
||||||
|
postgres: { }
|
||||||
|
sqlserver: { }
|
||||||
|
dm8: { }
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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:
|
||||||
|
# wget https://download.dameng.com/eco/dm8/dm8_20230808_rev197096_x86_rh6_64_single.tar
|
||||||
|
# docker load -i dm8_20230808_rev197096_x86_rh6_64_single.tar
|
||||||
|
image: dm8_single:dm8_20230808_rev197096_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
|
||||||
|
# docker compose exec dm8 bash -c "exec /opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/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
|
|
@ -14,7 +14,7 @@
|
||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>2.0.1-snapshot</revision>
|
<revision>2.1.0-snapshot</revision>
|
||||||
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
|
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
|
||||||
<!-- 统一依赖管理 -->
|
<!-- 统一依赖管理 -->
|
||||||
<spring.boot.version>3.2.2</spring.boot.version>
|
<spring.boot.version>3.2.2</spring.boot.version>
|
||||||
|
@ -27,13 +27,13 @@
|
||||||
<mybatis-plus-generator.version>3.5.5</mybatis-plus-generator.version>
|
<mybatis-plus-generator.version>3.5.5</mybatis-plus-generator.version>
|
||||||
<dynamic-datasource.version>4.3.0</dynamic-datasource.version>
|
<dynamic-datasource.version>4.3.0</dynamic-datasource.version>
|
||||||
<mybatis-plus-join.version>1.4.10</mybatis-plus-join.version>
|
<mybatis-plus-join.version>1.4.10</mybatis-plus-join.version>
|
||||||
|
<easy-trans.version>2.2.11</easy-trans.version>
|
||||||
<redisson.version>3.26.0</redisson.version>
|
<redisson.version>3.26.0</redisson.version>
|
||||||
<dm8.jdbc.version>8.1.3.62</dm8.jdbc.version>
|
<dm8.jdbc.version>8.1.3.62</dm8.jdbc.version>
|
||||||
<!-- 消息队列 -->
|
<!-- 消息队列 -->
|
||||||
<rocketmq-spring.version>2.2.3</rocketmq-spring.version>
|
<rocketmq-spring.version>2.3.0</rocketmq-spring.version>
|
||||||
<!-- 服务保障相关 -->
|
<!-- 服务保障相关 -->
|
||||||
<lock4j.version>2.2.7</lock4j.version>
|
<lock4j.version>2.2.7</lock4j.version>
|
||||||
<resilience4j.version>2.1.0</resilience4j.version>
|
|
||||||
<!-- 监控相关 -->
|
<!-- 监控相关 -->
|
||||||
<skywalking.version>9.0.0</skywalking.version>
|
<skywalking.version>9.0.0</skywalking.version>
|
||||||
<spring-boot-admin.version>3.2.1</spring-boot-admin.version>
|
<spring-boot-admin.version>3.2.1</spring-boot-admin.version>
|
||||||
|
@ -91,11 +91,6 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 业务组件 -->
|
<!-- 业务组件 -->
|
||||||
<dependency>
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.mouzt</groupId>
|
<groupId>io.github.mouzt</groupId>
|
||||||
<artifactId>bizlog-sdk</artifactId>
|
<artifactId>bizlog-sdk</artifactId>
|
||||||
|
@ -199,6 +194,32 @@
|
||||||
<version>${mybatis-plus-join.version}</version>
|
<version>${mybatis-plus-join.version}</version>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
||||||
|
@ -262,17 +283,6 @@
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.github.resilience4j</groupId>
|
|
||||||
<artifactId>resilience4j-ratelimiter</artifactId>
|
|
||||||
<version>${resilience4j.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.github.resilience4j</groupId>
|
|
||||||
<artifactId>resilience4j-spring-boot2</artifactId>
|
|
||||||
<version>${resilience4j.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 监控相关 -->
|
<!-- 监控相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
@ -435,7 +445,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara.hutool</groupId>
|
<groupId>org.dromara.hutool</groupId>
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-extra</artifactId>
|
||||||
<version>${hutool-6.version}</version>
|
<version>${hutool-6.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
@ -467,22 +477,6 @@
|
||||||
<version>${fastjson.version}</version>
|
<version>${fastjson.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.smallbun.screw</groupId>
|
|
||||||
<artifactId>screw-core</artifactId> <!-- 实现数据库文档 -->
|
|
||||||
<version>${screw.version}</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.freemarker</groupId>
|
|
||||||
<artifactId>freemarker</artifactId> <!-- 移除 Freemarker 依赖,采用 Velocity 作为模板引擎 -->
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.alibaba</groupId>
|
|
||||||
<artifactId>fastjson</artifactId> <!-- 最新版screw-core1.0.5依赖fastjson1.2.73存在漏洞,移除。 -->
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
|
@ -622,12 +616,6 @@
|
||||||
<version>${xercesImpl.version}</version>
|
<version>${xercesImpl.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- UReport2 报表-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.bstek.ureport</groupId>
|
|
||||||
<artifactId>ureport2-console</artifactId>
|
|
||||||
<version>${ureport2.version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
<?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>
|
|
||||||
|
|
||||||
<!-- 由于方便大家拷贝,使用不使用 yudao 作为 Maven parent -->
|
|
||||||
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-example</artifactId>
|
|
||||||
<version>1.0.0-snapshot</version>
|
|
||||||
<packaging>pom</packaging>
|
|
||||||
<modules>
|
|
||||||
<module>yudao-sso-demo-by-code</module>
|
|
||||||
<module>yudao-sso-demo-by-password</module>
|
|
||||||
</modules>
|
|
||||||
|
|
||||||
<name>${project.artifactId}</name>
|
|
||||||
<description>提供各种示例,例如说:SSO 单点登录</description>
|
|
||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -1,65 +0,0 @@
|
||||||
<?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>
|
|
||||||
|
|
||||||
<!-- 由于方便大家拷贝,使用不使用 yudao 作为 Maven parent -->
|
|
||||||
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-sso-demo-by-code</artifactId>
|
|
||||||
<version>1.0.0-snapshot</version>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<name>${project.artifactId}</name>
|
|
||||||
<description>基于授权码模式,如何实现 SSO 单点登录?</description>
|
|
||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<!-- Maven 相关 -->
|
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<!-- 统一依赖管理 -->
|
|
||||||
<spring.boot.version>3.2.0</spring.boot.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencyManagement>
|
|
||||||
<dependencies>
|
|
||||||
<!-- 统一依赖管理 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-dependencies</artifactId>
|
|
||||||
<version>${spring.boot.version}</version>
|
|
||||||
<type>pom</type>
|
|
||||||
<scope>import</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</dependencyManagement>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<!-- Web 相关 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-security</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.hutool</groupId>
|
|
||||||
<artifactId>hutool-all</artifactId>
|
|
||||||
<version>5.8.22</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -1,13 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
public class SSODemoApplication {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(SSODemoApplication.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
|
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
|
||||||
import org.springframework.http.*;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.Base64Utils;
|
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OAuth 2.0 客户端
|
|
||||||
*
|
|
||||||
* 对应调用 OAuth2OpenController 接口
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class OAuth2Client {
|
|
||||||
|
|
||||||
private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 租户编号
|
|
||||||
*
|
|
||||||
* 默认使用 1;如果使用别的租户,可以调整
|
|
||||||
*/
|
|
||||||
public static final Long TENANT_ID = 1L;
|
|
||||||
|
|
||||||
private static final String CLIENT_ID = "yudao-sso-demo-by-code";
|
|
||||||
private static final String CLIENT_SECRET = "test";
|
|
||||||
|
|
||||||
|
|
||||||
// @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
|
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用 code 授权码,获得访问令牌
|
|
||||||
*
|
|
||||||
* @param code 授权码
|
|
||||||
* @param redirectUri 重定向 URI
|
|
||||||
* @return 访问令牌
|
|
||||||
*/
|
|
||||||
public CommonResult<OAuth2AccessTokenRespDTO> postAccessToken(String code, String redirectUri) {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
headers.set("tenant-id", TENANT_ID.toString());
|
|
||||||
addClientHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
||||||
body.add("grant_type", "authorization_code");
|
|
||||||
body.add("code", code);
|
|
||||||
body.add("redirect_uri", redirectUri);
|
|
||||||
// body.add("state", ""); // 选填;填了会校验
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/token",
|
|
||||||
HttpMethod.POST,
|
|
||||||
new HttpEntity<>(body, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验访问令牌,并返回它的基本信息
|
|
||||||
*
|
|
||||||
* @param token 访问令牌
|
|
||||||
* @return 访问令牌的基本信息
|
|
||||||
*/
|
|
||||||
public CommonResult<OAuth2CheckTokenRespDTO> checkToken(String token) {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
headers.set("tenant-id", TENANT_ID.toString());
|
|
||||||
addClientHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
||||||
body.add("token", token);
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<OAuth2CheckTokenRespDTO>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/check-token",
|
|
||||||
HttpMethod.POST,
|
|
||||||
new HttpEntity<>(body, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<OAuth2CheckTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用刷新令牌,获得(刷新)访问令牌
|
|
||||||
*
|
|
||||||
* @param refreshToken 刷新令牌
|
|
||||||
* @return 访问令牌
|
|
||||||
*/
|
|
||||||
public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(String refreshToken) {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
headers.set("tenant-id", TENANT_ID.toString());
|
|
||||||
addClientHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
||||||
body.add("grant_type", "refresh_token");
|
|
||||||
body.add("refresh_token", refreshToken);
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/token",
|
|
||||||
HttpMethod.POST,
|
|
||||||
new HttpEntity<>(body, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除访问令牌
|
|
||||||
*
|
|
||||||
* @param token 访问令牌
|
|
||||||
* @return 成功
|
|
||||||
*/
|
|
||||||
public CommonResult<Boolean> revokeToken(String token) {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
headers.set("tenant-id", TENANT_ID.toString());
|
|
||||||
addClientHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
||||||
body.add("token", token);
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/token",
|
|
||||||
HttpMethod.DELETE,
|
|
||||||
new HttpEntity<>(body, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addClientHeader(HttpHeaders headers) {
|
|
||||||
// client 拼接,需要 BASE64 编码
|
|
||||||
String client = CLIENT_ID + ":" + CLIENT_SECRET;
|
|
||||||
client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8));
|
|
||||||
headers.add("Authorization", "Basic " + client);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
|
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
|
||||||
import org.springframework.http.*;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户 User 信息的客户端
|
|
||||||
*
|
|
||||||
* 对应调用 OAuth2UserController 接口
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class UserClient {
|
|
||||||
|
|
||||||
private static final String BASE_URL = "http://127.0.0.1:48080/admin-api//system/oauth2/user";
|
|
||||||
|
|
||||||
// @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
|
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
|
||||||
|
|
||||||
public CommonResult<UserInfoRespDTO> getUser() {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
|
|
||||||
addTokenHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<UserInfoRespDTO>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/get",
|
|
||||||
HttpMethod.GET,
|
|
||||||
new HttpEntity<>(body, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<UserInfoRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommonResult<Boolean> updateUser(UserUpdateReqDTO updateReqDTO) {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
||||||
headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
|
|
||||||
addTokenHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
// 使用 updateReqDTO 即可
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/update",
|
|
||||||
HttpMethod.PUT,
|
|
||||||
new HttpEntity<>(updateReqDTO, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static void addTokenHeader(HttpHeaders headers) {
|
|
||||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
|
||||||
Assert.notNull(loginUser, "登录用户不能为空");
|
|
||||||
headers.add("Authorization", "Bearer " + loginUser.getAccessToken());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通用返回
|
|
||||||
*
|
|
||||||
* @param <T> 数据泛型
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CommonResult<T> implements Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误码
|
|
||||||
*/
|
|
||||||
private Integer code;
|
|
||||||
/**
|
|
||||||
* 返回数据
|
|
||||||
*/
|
|
||||||
private T data;
|
|
||||||
/**
|
|
||||||
* 错误提示,用户可阅读
|
|
||||||
*/
|
|
||||||
private String msg;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto.oauth2;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问令牌 Response DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class OAuth2AccessTokenRespDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问令牌
|
|
||||||
*/
|
|
||||||
@JsonProperty("access_token")
|
|
||||||
private String accessToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新令牌
|
|
||||||
*/
|
|
||||||
@JsonProperty("refresh_token")
|
|
||||||
private String refreshToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 令牌类型
|
|
||||||
*/
|
|
||||||
@JsonProperty("token_type")
|
|
||||||
private String tokenType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 过期时间;单位:秒
|
|
||||||
*/
|
|
||||||
@JsonProperty("expires_in")
|
|
||||||
private Long expiresIn;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 授权范围;如果多个授权范围,使用空格分隔
|
|
||||||
*/
|
|
||||||
private String scope;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto.oauth2;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验令牌 Response DTO
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class OAuth2CheckTokenRespDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户编号
|
|
||||||
*/
|
|
||||||
@JsonProperty("user_id")
|
|
||||||
private Long userId;
|
|
||||||
/**
|
|
||||||
* 用户类型
|
|
||||||
*/
|
|
||||||
@JsonProperty("user_type")
|
|
||||||
private Integer userType;
|
|
||||||
/**
|
|
||||||
* 租户编号
|
|
||||||
*/
|
|
||||||
@JsonProperty("tenant_id")
|
|
||||||
private Long tenantId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端编号
|
|
||||||
*/
|
|
||||||
@JsonProperty("client_id")
|
|
||||||
private String clientId;
|
|
||||||
/**
|
|
||||||
* 授权范围
|
|
||||||
*/
|
|
||||||
private List<String> scopes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问令牌
|
|
||||||
*/
|
|
||||||
@JsonProperty("access_token")
|
|
||||||
private String accessToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 过期时间
|
|
||||||
*
|
|
||||||
* 时间戳 / 1000,即单位:秒
|
|
||||||
*/
|
|
||||||
private Long exp;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto.user;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得用户基本信息 Response dto
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class UserInfoRespDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户编号
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户账号
|
|
||||||
*/
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户昵称
|
|
||||||
*/
|
|
||||||
private String nickname;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户邮箱
|
|
||||||
*/
|
|
||||||
private String email;
|
|
||||||
/**
|
|
||||||
* 手机号码
|
|
||||||
*/
|
|
||||||
private String mobile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户性别
|
|
||||||
*/
|
|
||||||
private Integer sex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户头像
|
|
||||||
*/
|
|
||||||
private String avatar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所在部门
|
|
||||||
*/
|
|
||||||
private Dept dept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所属岗位数组
|
|
||||||
*/
|
|
||||||
private List<Post> posts;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class Dept {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门编号
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门名称
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 岗位
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class Post {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 岗位编号
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 岗位名称
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto.user;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新用户基本信息 Request DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class UserUpdateReqDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户昵称
|
|
||||||
*/
|
|
||||||
private String nickname;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户邮箱
|
|
||||||
*/
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手机号码
|
|
||||||
*/
|
|
||||||
private String mobile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户性别
|
|
||||||
*/
|
|
||||||
private Integer sex;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.controller;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/auth")
|
|
||||||
public class AuthController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private OAuth2Client oauth2Client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用 code 访问令牌,获得访问令牌
|
|
||||||
*
|
|
||||||
* @param code 授权码
|
|
||||||
* @param redirectUri 重定向 URI
|
|
||||||
* @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
|
|
||||||
*/
|
|
||||||
@PostMapping("/login-by-code")
|
|
||||||
public CommonResult<OAuth2AccessTokenRespDTO> loginByCode(@RequestParam("code") String code,
|
|
||||||
@RequestParam("redirectUri") String redirectUri) {
|
|
||||||
return oauth2Client.postAccessToken(code, redirectUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用刷新令牌,获得(刷新)访问令牌
|
|
||||||
*
|
|
||||||
* @param refreshToken 刷新令牌
|
|
||||||
* @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
|
|
||||||
*/
|
|
||||||
@PostMapping("/refresh-token")
|
|
||||||
public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
|
|
||||||
return oauth2Client.refreshToken(refreshToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 退出登录
|
|
||||||
*
|
|
||||||
* @param request 请求
|
|
||||||
* @return 成功
|
|
||||||
*/
|
|
||||||
@PostMapping("/logout")
|
|
||||||
public CommonResult<Boolean> logout(HttpServletRequest request) {
|
|
||||||
String token = SecurityUtils.obtainAuthorization(request, "Authorization");
|
|
||||||
if (StrUtil.isNotBlank(token)) {
|
|
||||||
return oauth2Client.revokeToken(token);
|
|
||||||
}
|
|
||||||
// 返回成功
|
|
||||||
return new CommonResult<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.controller;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.UserClient;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/user")
|
|
||||||
public class UserController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private UserClient userClient;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得当前登录用户的基本信息
|
|
||||||
*
|
|
||||||
* @return 用户信息;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
|
|
||||||
*/
|
|
||||||
@GetMapping("/get")
|
|
||||||
public CommonResult<UserInfoRespDTO> getUser() {
|
|
||||||
return userClient.getUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新当前登录用户的昵称
|
|
||||||
*
|
|
||||||
* @param nickname 昵称
|
|
||||||
* @return 成功
|
|
||||||
*/
|
|
||||||
@PutMapping("/update")
|
|
||||||
public CommonResult<Boolean> updateUser(@RequestParam("nickname") String nickname) {
|
|
||||||
UserUpdateReqDTO updateReqDTO = new UserUpdateReqDTO(nickname, null, null, null);
|
|
||||||
return userClient.updateUser(updateReqDTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.config;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.filter.TokenAuthenticationFilter;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.handler.AccessDeniedHandlerImpl;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class SecurityConfiguration{
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private TokenAuthenticationFilter tokenAuthenticationFilter;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AccessDeniedHandlerImpl accessDeniedHandler;
|
|
||||||
@Resource
|
|
||||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
|
||||||
// 设置 URL 安全权限
|
|
||||||
httpSecurity
|
|
||||||
// 开启跨域
|
|
||||||
.cors(Customizer.withDefaults())
|
|
||||||
// CSRF 禁用,因为不使用 Session
|
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
|
||||||
// 一堆自定义的 Spring Security 处理器
|
|
||||||
.exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint)
|
|
||||||
.accessDeniedHandler(accessDeniedHandler));
|
|
||||||
|
|
||||||
// 设置每个请求的权限
|
|
||||||
httpSecurity.authorizeHttpRequests(c -> c
|
|
||||||
// 1. 静态资源,可匿名访问
|
|
||||||
.requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
|
|
||||||
// 2. 登录相关的接口,可匿名访问
|
|
||||||
.requestMatchers("/auth/login-by-code").permitAll()
|
|
||||||
.requestMatchers("/auth/refresh-token").permitAll()
|
|
||||||
.requestMatchers("/auth/logout").permitAll())
|
|
||||||
// 3. 兜底规则,必须认证
|
|
||||||
.authorizeHttpRequests(c -> c.anyRequest().authenticated());
|
|
||||||
|
|
||||||
// 添加 Token Filter
|
|
||||||
httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
|
||||||
return httpSecurity.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 登录用户信息
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class LoginUser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户编号
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
/**
|
|
||||||
* 用户类型
|
|
||||||
*/
|
|
||||||
private Integer userType;
|
|
||||||
/**
|
|
||||||
* 租户编号
|
|
||||||
*/
|
|
||||||
private Long tenantId;
|
|
||||||
/**
|
|
||||||
* 授权范围
|
|
||||||
*/
|
|
||||||
private List<String> scopes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问令牌
|
|
||||||
*/
|
|
||||||
private String accessToken;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.filter;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Token 过滤器,验证 token 的有效性
|
|
||||||
* 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private OAuth2Client oauth2Client;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
|
||||||
FilterChain filterChain) throws ServletException, IOException {
|
|
||||||
// 1. 获得访问令牌
|
|
||||||
String token = SecurityUtils.obtainAuthorization(request, "Authorization");
|
|
||||||
if (StringUtils.hasText(token)) {
|
|
||||||
// 2. 基于 token 构建登录用户
|
|
||||||
LoginUser loginUser = buildLoginUserByToken(token);
|
|
||||||
// 3. 设置当前用户
|
|
||||||
if (loginUser != null) {
|
|
||||||
SecurityUtils.setLoginUser(loginUser, request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 继续过滤链
|
|
||||||
filterChain.doFilter(request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LoginUser buildLoginUserByToken(String token) {
|
|
||||||
try {
|
|
||||||
CommonResult<OAuth2CheckTokenRespDTO> accessTokenResult = oauth2Client.checkToken(token);
|
|
||||||
OAuth2CheckTokenRespDTO accessToken = accessTokenResult.getData();
|
|
||||||
if (accessToken == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// 构建登录用户
|
|
||||||
return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
|
|
||||||
.setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
|
|
||||||
.setAccessToken(accessToken.getAccessToken());
|
|
||||||
} catch (Exception exception) {
|
|
||||||
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.handler;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。
|
|
||||||
*
|
|
||||||
* 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@SuppressWarnings("JavadocReference")
|
|
||||||
@Slf4j
|
|
||||||
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
|
|
||||||
throws IOException, ServletException {
|
|
||||||
// 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
|
|
||||||
log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
|
|
||||||
SecurityUtils.getLoginUserId(), e);
|
|
||||||
// 返回 403
|
|
||||||
CommonResult<Object> result = new CommonResult<>();
|
|
||||||
result.setCode(HttpStatus.FORBIDDEN.value());
|
|
||||||
result.setMsg("没有该操作权限");
|
|
||||||
ServletUtils.writeJSON(response, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.handler;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.security.core.AuthenticationException;
|
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
|
|
||||||
*
|
|
||||||
* 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@Slf4j
|
|
||||||
@SuppressWarnings("JavadocReference") // 忽略文档引用报错
|
|
||||||
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
|
||||||
log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
|
|
||||||
// 返回 401
|
|
||||||
CommonResult<Object> result = new CommonResult<>();
|
|
||||||
result.setCode(HttpStatus.UNAUTHORIZED.value());
|
|
||||||
result.setMsg("账号未登录");
|
|
||||||
ServletUtils.writeJSON(response, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.util;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 安全服务工具类
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
public class SecurityUtils {
|
|
||||||
|
|
||||||
public static final String AUTHORIZATION_BEARER = "Bearer";
|
|
||||||
|
|
||||||
private SecurityUtils() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从请求中,获得认证 Token
|
|
||||||
*
|
|
||||||
* @param request 请求
|
|
||||||
* @param header 认证 Token 对应的 Header 名字
|
|
||||||
* @return 认证 Token
|
|
||||||
*/
|
|
||||||
public static String obtainAuthorization(HttpServletRequest request, String header) {
|
|
||||||
String authorization = request.getHeader(header);
|
|
||||||
if (!StringUtils.hasText(authorization)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
|
|
||||||
if (index == -1) { // 未找到
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return authorization.substring(index + 7).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得当前认证信息
|
|
||||||
*
|
|
||||||
* @return 认证信息
|
|
||||||
*/
|
|
||||||
public static Authentication getAuthentication() {
|
|
||||||
SecurityContext context = SecurityContextHolder.getContext();
|
|
||||||
if (context == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return context.getAuthentication();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前用户
|
|
||||||
*
|
|
||||||
* @return 当前用户
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static LoginUser getLoginUser() {
|
|
||||||
Authentication authentication = getAuthentication();
|
|
||||||
if (authentication == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得当前用户的编号,从上下文中
|
|
||||||
*
|
|
||||||
* @return 用户编号
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static Long getLoginUserId() {
|
|
||||||
LoginUser loginUser = getLoginUser();
|
|
||||||
return loginUser != null ? loginUser.getId() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置当前用户
|
|
||||||
*
|
|
||||||
* @param loginUser 登录用户
|
|
||||||
* @param request 请求
|
|
||||||
*/
|
|
||||||
public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
|
|
||||||
// 创建 Authentication,并设置到上下文
|
|
||||||
Authentication authentication = buildAuthentication(loginUser, request);
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
|
|
||||||
// 创建 UsernamePasswordAuthenticationToken 对象
|
|
||||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
|
|
||||||
loginUser, null, Collections.emptyList());
|
|
||||||
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
|
||||||
return authenticationToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.util;
|
|
||||||
|
|
||||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
|
||||||
import cn.hutool.extra.servlet.ServletUtil;
|
|
||||||
import cn.hutool.json.JSONUtil;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端工具类
|
|
||||||
*
|
|
||||||
* @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 = JSONUtil.toJsonStr(object);
|
|
||||||
JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void write(HttpServletResponse response, String text, String contentType) {
|
|
||||||
JakartaServletUtil.write(response, text, contentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
server:
|
|
||||||
port: 18080
|
|
|
@ -1,61 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>SSO 授权后的回调页</title>
|
|
||||||
<!-- jQuery:操作 dom、发起请求等 -->
|
|
||||||
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
|
|
||||||
<!-- 工具类 -->
|
|
||||||
<script type="application/javascript">
|
|
||||||
(function ($) {
|
|
||||||
/**
|
|
||||||
* 获得 URL 的指定参数的值
|
|
||||||
*
|
|
||||||
* @param name 参数名
|
|
||||||
* @returns 参数值
|
|
||||||
*/
|
|
||||||
$.getUrlParam = function (name) {
|
|
||||||
const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
|
|
||||||
const r = window.location.search.substr(1).match(reg);
|
|
||||||
if (r != null) return unescape(r[2]); return null;
|
|
||||||
}
|
|
||||||
})(jQuery);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="application/javascript">
|
|
||||||
$(function () {
|
|
||||||
// 获得 code 授权码
|
|
||||||
const code = $.getUrlParam('code');
|
|
||||||
if (!code) {
|
|
||||||
alert('获取不到 code 参数,请排查!')
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交
|
|
||||||
const redirectUri = 'http://127.0.0.1:18080/callback.html'; // 需要修改成,你回调的地址,就是在 index.html 拼接的 redirectUri
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:18080/auth/login-by-code?code=" + code
|
|
||||||
+ '&redirectUri=' + redirectUri,
|
|
||||||
method: 'POST',
|
|
||||||
success: function( result ) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('获得访问令牌失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
alert('获得访问令牌成功!点击确认,跳转回首页')
|
|
||||||
|
|
||||||
// 设置到 localStorage 中
|
|
||||||
localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
|
|
||||||
localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
|
|
||||||
|
|
||||||
// 跳转回首页
|
|
||||||
window.location.href = '/index.html';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
正在使用 code 授权码,进行 accessToken 访问令牌的获取
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,159 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>首页</title>
|
|
||||||
<!-- jQuery:操作 dom、发起请求等 -->
|
|
||||||
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
|
|
||||||
|
|
||||||
<script type="application/javascript">
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 跳转单点登录
|
|
||||||
*/
|
|
||||||
function ssoLogin() {
|
|
||||||
const clientId = 'yudao-sso-demo-by-code'; // 可以改写成,你的 clientId
|
|
||||||
const redirectUri = encodeURIComponent('http://127.0.0.1:18080/callback.html'); // 注意,需要使用 encodeURIComponent 编码地址
|
|
||||||
const responseType = 'code'; // 1)授权码模式,对应 code;2)简化模式,对应 token
|
|
||||||
window.location.href = 'http://127.0.0.1:1024/sso?client_id=' + clientId
|
|
||||||
+ '&redirect_uri=' + redirectUri
|
|
||||||
+ '&response_type=' + responseType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改昵称
|
|
||||||
*/
|
|
||||||
function updateNickname() {
|
|
||||||
const nickname = prompt("请输入新的昵称", "");
|
|
||||||
if (!nickname) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 更新用户的昵称
|
|
||||||
const accessToken = localStorage.getItem('ACCESS-TOKEN');
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:18080/user/update?nickname=" + nickname,
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + accessToken
|
|
||||||
},
|
|
||||||
success: function (result) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('更新昵称失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
alert('更新昵称成功!');
|
|
||||||
$('#nicknameSpan').html(nickname);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新令牌
|
|
||||||
*/
|
|
||||||
function refreshToken() {
|
|
||||||
const refreshToken = localStorage.getItem('REFRESH-TOKEN');
|
|
||||||
if (!refreshToken) {
|
|
||||||
alert("获取不到刷新令牌");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:18080/auth/refresh-token?refreshToken=" + refreshToken,
|
|
||||||
method: 'POST',
|
|
||||||
success: function (result) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('刷新访问令牌失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
alert('更新访问令牌成功!');
|
|
||||||
$('#accessTokenSpan').html(result.data.access_token);
|
|
||||||
|
|
||||||
// 设置到 localStorage 中
|
|
||||||
localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
|
|
||||||
localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 登出,删除访问令牌
|
|
||||||
*/
|
|
||||||
function logout() {
|
|
||||||
const accessToken = localStorage.getItem('ACCESS-TOKEN');
|
|
||||||
if (!accessToken) {
|
|
||||||
location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:18080/auth/logout",
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + accessToken
|
|
||||||
},
|
|
||||||
success: function (result) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('退出登录失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
alert('退出登录成功!');
|
|
||||||
// 删除 localStorage 中
|
|
||||||
localStorage.removeItem('ACCESS-TOKEN');
|
|
||||||
localStorage.removeItem('REFRESH-TOKEN');
|
|
||||||
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function () {
|
|
||||||
const accessToken = localStorage.getItem('ACCESS-TOKEN');
|
|
||||||
// 情况一:未登录
|
|
||||||
if (!accessToken) {
|
|
||||||
$('#noLoginDiv').css("display", "block");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 情况二:已登录
|
|
||||||
$('#yesLoginDiv').css("display", "block");
|
|
||||||
$('#accessTokenSpan').html(accessToken);
|
|
||||||
// 获得登录用户的信息
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:18080/user/get",
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + accessToken
|
|
||||||
},
|
|
||||||
success: function (result) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('获得个人信息失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$('#nicknameSpan').html(result.data.nickname);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- 情况一:未登录:1)跳转 ruoyi-vue-pro 的 SSO 登录页 -->
|
|
||||||
<div id="noLoginDiv" style="display: none">
|
|
||||||
您未登录,点击 <a href="#" onclick="ssoLogin()">跳转 </a> SSO 单点登录
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 -->
|
|
||||||
<div id="yesLoginDiv" style="display: none">
|
|
||||||
您已登录!<button onclick="logout()">退出登录</button> <br />
|
|
||||||
昵称:<span id="nicknameSpan"> 加载中... </span> <button onclick="updateNickname()">修改昵称</button> <br />
|
|
||||||
访问令牌:<span id="accessTokenSpan"> 加载中... </span> <button onclick="refreshToken()">刷新令牌</button> <br />
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<style>
|
|
||||||
body { /** 页面居中 */
|
|
||||||
border-radius: 20px;
|
|
||||||
height: 350px;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%,-50%);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</html>
|
|
|
@ -1,65 +0,0 @@
|
||||||
<?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>
|
|
||||||
|
|
||||||
<!-- 由于方便大家拷贝,使用不使用 yudao 作为 Maven parent -->
|
|
||||||
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-sso-demo-by-password</artifactId>
|
|
||||||
<version>1.0.0-snapshot</version>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<name>${project.artifactId}</name>
|
|
||||||
<description>基于密码模式,如何实现 SSO 单点登录?</description>
|
|
||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<!-- Maven 相关 -->
|
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<!-- 统一依赖管理 -->
|
|
||||||
<spring.boot.version>3.2.0</spring.boot.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencyManagement>
|
|
||||||
<dependencies>
|
|
||||||
<!-- 统一依赖管理 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-dependencies</artifactId>
|
|
||||||
<version>${spring.boot.version}</version>
|
|
||||||
<type>pom</type>
|
|
||||||
<scope>import</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</dependencyManagement>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<!-- Web 相关 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-security</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.hutool</groupId>
|
|
||||||
<artifactId>hutool-all</artifactId>
|
|
||||||
<version>5.8.22</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -1,13 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
public class SSODemoApplication {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(SSODemoApplication.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
|
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
|
||||||
import org.springframework.http.*;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.Base64Utils;
|
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OAuth 2.0 客户端
|
|
||||||
*
|
|
||||||
* 对应调用 OAuth2OpenController 接口
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class OAuth2Client {
|
|
||||||
|
|
||||||
private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 租户编号
|
|
||||||
*
|
|
||||||
* 默认使用 1;如果使用别的租户,可以调整
|
|
||||||
*/
|
|
||||||
public static final Long TENANT_ID = 1L;
|
|
||||||
|
|
||||||
private static final String CLIENT_ID = "yudao-sso-demo-by-password";
|
|
||||||
private static final String CLIENT_SECRET = "test";
|
|
||||||
|
|
||||||
|
|
||||||
// @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
|
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验访问令牌,并返回它的基本信息
|
|
||||||
*
|
|
||||||
* @param token 访问令牌
|
|
||||||
* @return 访问令牌的基本信息
|
|
||||||
*/
|
|
||||||
public CommonResult<OAuth2CheckTokenRespDTO> checkToken(String token) {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
headers.set("tenant-id", TENANT_ID.toString());
|
|
||||||
addClientHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
||||||
body.add("token", token);
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<OAuth2CheckTokenRespDTO>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/check-token",
|
|
||||||
HttpMethod.POST,
|
|
||||||
new HttpEntity<>(body, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<OAuth2CheckTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用刷新令牌,获得(刷新)访问令牌
|
|
||||||
*
|
|
||||||
* @param refreshToken 刷新令牌
|
|
||||||
* @return 访问令牌
|
|
||||||
*/
|
|
||||||
public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(String refreshToken) {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
headers.set("tenant-id", TENANT_ID.toString());
|
|
||||||
addClientHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
||||||
body.add("grant_type", "refresh_token");
|
|
||||||
body.add("refresh_token", refreshToken);
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/token",
|
|
||||||
HttpMethod.POST,
|
|
||||||
new HttpEntity<>(body, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除访问令牌
|
|
||||||
*
|
|
||||||
* @param token 访问令牌
|
|
||||||
* @return 成功
|
|
||||||
*/
|
|
||||||
public CommonResult<Boolean> revokeToken(String token) {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
headers.set("tenant-id", TENANT_ID.toString());
|
|
||||||
addClientHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
||||||
body.add("token", token);
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/token",
|
|
||||||
HttpMethod.DELETE,
|
|
||||||
new HttpEntity<>(body, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addClientHeader(HttpHeaders headers) {
|
|
||||||
// client 拼接,需要 BASE64 编码
|
|
||||||
String client = CLIENT_ID + ":" + CLIENT_SECRET;
|
|
||||||
client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8));
|
|
||||||
headers.add("Authorization", "Basic " + client);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
|
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
|
||||||
import org.springframework.http.*;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户 User 信息的客户端
|
|
||||||
*
|
|
||||||
* 对应调用 OAuth2UserController 接口
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class UserClient {
|
|
||||||
|
|
||||||
private static final String BASE_URL = "http://127.0.0.1:48080/admin-api//system/oauth2/user";
|
|
||||||
|
|
||||||
// @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
|
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
|
||||||
|
|
||||||
public CommonResult<UserInfoRespDTO> getUser() {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
||||||
headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
|
|
||||||
addTokenHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<UserInfoRespDTO>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/get",
|
|
||||||
HttpMethod.GET,
|
|
||||||
new HttpEntity<>(body, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<UserInfoRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommonResult<Boolean> updateUser(UserUpdateReqDTO updateReqDTO) {
|
|
||||||
// 1.1 构建请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
||||||
headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
|
|
||||||
addTokenHeader(headers);
|
|
||||||
// 1.2 构建请求参数
|
|
||||||
// 使用 updateReqDTO 即可
|
|
||||||
|
|
||||||
// 2. 执行请求
|
|
||||||
ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
|
|
||||||
BASE_URL + "/update",
|
|
||||||
HttpMethod.PUT,
|
|
||||||
new HttpEntity<>(updateReqDTO, headers),
|
|
||||||
new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
|
|
||||||
Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
|
|
||||||
return exchange.getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static void addTokenHeader(HttpHeaders headers) {
|
|
||||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
|
||||||
Assert.notNull(loginUser, "登录用户不能为空");
|
|
||||||
headers.add("Authorization", "Bearer " + loginUser.getAccessToken());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通用返回
|
|
||||||
*
|
|
||||||
* @param <T> 数据泛型
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class CommonResult<T> implements Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误码
|
|
||||||
*/
|
|
||||||
private Integer code;
|
|
||||||
/**
|
|
||||||
* 返回数据
|
|
||||||
*/
|
|
||||||
private T data;
|
|
||||||
/**
|
|
||||||
* 错误提示,用户可阅读
|
|
||||||
*/
|
|
||||||
private String msg;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto.oauth2;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问令牌 Response DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class OAuth2AccessTokenRespDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问令牌
|
|
||||||
*/
|
|
||||||
@JsonProperty("access_token")
|
|
||||||
private String accessToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新令牌
|
|
||||||
*/
|
|
||||||
@JsonProperty("refresh_token")
|
|
||||||
private String refreshToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 令牌类型
|
|
||||||
*/
|
|
||||||
@JsonProperty("token_type")
|
|
||||||
private String tokenType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 过期时间;单位:秒
|
|
||||||
*/
|
|
||||||
@JsonProperty("expires_in")
|
|
||||||
private Long expiresIn;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 授权范围;如果多个授权范围,使用空格分隔
|
|
||||||
*/
|
|
||||||
private String scope;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto.oauth2;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验令牌 Response DTO
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class OAuth2CheckTokenRespDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户编号
|
|
||||||
*/
|
|
||||||
@JsonProperty("user_id")
|
|
||||||
private Long userId;
|
|
||||||
/**
|
|
||||||
* 用户类型
|
|
||||||
*/
|
|
||||||
@JsonProperty("user_type")
|
|
||||||
private Integer userType;
|
|
||||||
/**
|
|
||||||
* 租户编号
|
|
||||||
*/
|
|
||||||
@JsonProperty("tenant_id")
|
|
||||||
private Long tenantId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端编号
|
|
||||||
*/
|
|
||||||
@JsonProperty("client_id")
|
|
||||||
private String clientId;
|
|
||||||
/**
|
|
||||||
* 授权范围
|
|
||||||
*/
|
|
||||||
private List<String> scopes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问令牌
|
|
||||||
*/
|
|
||||||
@JsonProperty("access_token")
|
|
||||||
private String accessToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 过期时间
|
|
||||||
*
|
|
||||||
* 时间戳 / 1000,即单位:秒
|
|
||||||
*/
|
|
||||||
private Long exp;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto.user;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得用户基本信息 Response dto
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class UserInfoRespDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户编号
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户账号
|
|
||||||
*/
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户昵称
|
|
||||||
*/
|
|
||||||
private String nickname;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户邮箱
|
|
||||||
*/
|
|
||||||
private String email;
|
|
||||||
/**
|
|
||||||
* 手机号码
|
|
||||||
*/
|
|
||||||
private String mobile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户性别
|
|
||||||
*/
|
|
||||||
private Integer sex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户头像
|
|
||||||
*/
|
|
||||||
private String avatar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所在部门
|
|
||||||
*/
|
|
||||||
private Dept dept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所属岗位数组
|
|
||||||
*/
|
|
||||||
private List<Post> posts;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class Dept {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门编号
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门名称
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 岗位
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class Post {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 岗位编号
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 岗位名称
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.client.dto.user;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新用户基本信息 Request DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class UserUpdateReqDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户昵称
|
|
||||||
*/
|
|
||||||
private String nickname;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户邮箱
|
|
||||||
*/
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手机号码
|
|
||||||
*/
|
|
||||||
private String mobile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户性别
|
|
||||||
*/
|
|
||||||
private Integer sex;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.controller;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/auth")
|
|
||||||
public class AuthController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private OAuth2Client oauth2Client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用刷新令牌,获得(刷新)访问令牌
|
|
||||||
*
|
|
||||||
* @param refreshToken 刷新令牌
|
|
||||||
* @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
|
|
||||||
*/
|
|
||||||
@PostMapping("/refresh-token")
|
|
||||||
public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
|
|
||||||
return oauth2Client.refreshToken(refreshToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 退出登录
|
|
||||||
*
|
|
||||||
* @param request 请求
|
|
||||||
* @return 成功
|
|
||||||
*/
|
|
||||||
@PostMapping("/logout")
|
|
||||||
public CommonResult<Boolean> logout(HttpServletRequest request) {
|
|
||||||
String token = SecurityUtils.obtainAuthorization(request, "Authorization");
|
|
||||||
if (StrUtil.isNotBlank(token)) {
|
|
||||||
return oauth2Client.revokeToken(token);
|
|
||||||
}
|
|
||||||
// 返回成功
|
|
||||||
return new CommonResult<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.controller;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.UserClient;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/user")
|
|
||||||
public class UserController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private UserClient userClient;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得当前登录用户的基本信息
|
|
||||||
*
|
|
||||||
* @return 用户信息;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
|
|
||||||
*/
|
|
||||||
@GetMapping("/get")
|
|
||||||
public CommonResult<UserInfoRespDTO> getUser() {
|
|
||||||
return userClient.getUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新当前登录用户的昵称
|
|
||||||
*
|
|
||||||
* @param nickname 昵称
|
|
||||||
* @return 成功
|
|
||||||
*/
|
|
||||||
@PutMapping("/update")
|
|
||||||
public CommonResult<Boolean> updateUser(@RequestParam("nickname") String nickname) {
|
|
||||||
UserUpdateReqDTO updateReqDTO = new UserUpdateReqDTO(nickname, null, null, null);
|
|
||||||
return userClient.updateUser(updateReqDTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.config;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.filter.TokenAuthenticationFilter;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.handler.AccessDeniedHandlerImpl;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class SecurityConfiguration {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private TokenAuthenticationFilter tokenAuthenticationFilter;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AccessDeniedHandlerImpl accessDeniedHandler;
|
|
||||||
@Resource
|
|
||||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
|
||||||
// 设置 URL 安全权限
|
|
||||||
httpSecurity
|
|
||||||
// 开启跨域
|
|
||||||
.cors(Customizer.withDefaults())
|
|
||||||
// CSRF 禁用,因为不使用 Session
|
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
|
||||||
// 一堆自定义的 Spring Security 处理器
|
|
||||||
.exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint)
|
|
||||||
.accessDeniedHandler(accessDeniedHandler));
|
|
||||||
|
|
||||||
// 设置每个请求的权限
|
|
||||||
httpSecurity.authorizeHttpRequests(c -> c
|
|
||||||
// 1. 静态资源,可匿名访问
|
|
||||||
.requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
|
|
||||||
// 2. 登录相关的接口,可匿名访问
|
|
||||||
.requestMatchers("/auth/login-by-code").permitAll()
|
|
||||||
.requestMatchers("/auth/refresh-token").permitAll()
|
|
||||||
.requestMatchers("/auth/logout").permitAll())
|
|
||||||
// 3. 兜底规则,必须认证
|
|
||||||
.authorizeHttpRequests(c -> c.anyRequest().authenticated());
|
|
||||||
|
|
||||||
// 添加 Token Filter
|
|
||||||
httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
|
||||||
return httpSecurity.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 登录用户信息
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class LoginUser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户编号
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
/**
|
|
||||||
* 用户类型
|
|
||||||
*/
|
|
||||||
private Integer userType;
|
|
||||||
/**
|
|
||||||
* 租户编号
|
|
||||||
*/
|
|
||||||
private Long tenantId;
|
|
||||||
/**
|
|
||||||
* 授权范围
|
|
||||||
*/
|
|
||||||
private List<String> scopes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问令牌
|
|
||||||
*/
|
|
||||||
private String accessToken;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.filter;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Token 过滤器,验证 token 的有效性
|
|
||||||
* 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private OAuth2Client oauth2Client;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
|
||||||
FilterChain filterChain) throws ServletException, IOException {
|
|
||||||
// 1. 获得访问令牌
|
|
||||||
String token = SecurityUtils.obtainAuthorization(request, "Authorization");
|
|
||||||
if (StringUtils.hasText(token)) {
|
|
||||||
// 2. 基于 token 构建登录用户
|
|
||||||
LoginUser loginUser = buildLoginUserByToken(token);
|
|
||||||
// 3. 设置当前用户
|
|
||||||
if (loginUser != null) {
|
|
||||||
SecurityUtils.setLoginUser(loginUser, request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 继续过滤链
|
|
||||||
filterChain.doFilter(request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LoginUser buildLoginUserByToken(String token) {
|
|
||||||
try {
|
|
||||||
CommonResult<OAuth2CheckTokenRespDTO> accessTokenResult = oauth2Client.checkToken(token);
|
|
||||||
OAuth2CheckTokenRespDTO accessToken = accessTokenResult.getData();
|
|
||||||
if (accessToken == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// 构建登录用户
|
|
||||||
return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
|
|
||||||
.setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
|
|
||||||
.setAccessToken(accessToken.getAccessToken());
|
|
||||||
} catch (Exception exception) {
|
|
||||||
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.handler;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。
|
|
||||||
*
|
|
||||||
* 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@SuppressWarnings("JavadocReference")
|
|
||||||
@Slf4j
|
|
||||||
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
|
|
||||||
throws IOException, ServletException {
|
|
||||||
// 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
|
|
||||||
log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
|
|
||||||
SecurityUtils.getLoginUserId(), e);
|
|
||||||
// 返回 403
|
|
||||||
CommonResult<Object> result = new CommonResult<>();
|
|
||||||
result.setCode(HttpStatus.FORBIDDEN.value());
|
|
||||||
result.setMsg("没有该操作权限");
|
|
||||||
ServletUtils.writeJSON(response, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.handler;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.security.core.AuthenticationException;
|
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
|
|
||||||
*
|
|
||||||
* 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@Slf4j
|
|
||||||
@SuppressWarnings("JavadocReference") // 忽略文档引用报错
|
|
||||||
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
|
||||||
log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
|
|
||||||
// 返回 401
|
|
||||||
CommonResult<Object> result = new CommonResult<>();
|
|
||||||
result.setCode(HttpStatus.UNAUTHORIZED.value());
|
|
||||||
result.setMsg("账号未登录");
|
|
||||||
ServletUtils.writeJSON(response, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.util;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 安全服务工具类
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
public class SecurityUtils {
|
|
||||||
|
|
||||||
public static final String AUTHORIZATION_BEARER = "Bearer";
|
|
||||||
|
|
||||||
private SecurityUtils() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从请求中,获得认证 Token
|
|
||||||
*
|
|
||||||
* @param request 请求
|
|
||||||
* @param header 认证 Token 对应的 Header 名字
|
|
||||||
* @return 认证 Token
|
|
||||||
*/
|
|
||||||
public static String obtainAuthorization(HttpServletRequest request, String header) {
|
|
||||||
String authorization = request.getHeader(header);
|
|
||||||
if (!StringUtils.hasText(authorization)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
|
|
||||||
if (index == -1) { // 未找到
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return authorization.substring(index + 7).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得当前认证信息
|
|
||||||
*
|
|
||||||
* @return 认证信息
|
|
||||||
*/
|
|
||||||
public static Authentication getAuthentication() {
|
|
||||||
SecurityContext context = SecurityContextHolder.getContext();
|
|
||||||
if (context == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return context.getAuthentication();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前用户
|
|
||||||
*
|
|
||||||
* @return 当前用户
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static LoginUser getLoginUser() {
|
|
||||||
Authentication authentication = getAuthentication();
|
|
||||||
if (authentication == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得当前用户的编号,从上下文中
|
|
||||||
*
|
|
||||||
* @return 用户编号
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static Long getLoginUserId() {
|
|
||||||
LoginUser loginUser = getLoginUser();
|
|
||||||
return loginUser != null ? loginUser.getId() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置当前用户
|
|
||||||
*
|
|
||||||
* @param loginUser 登录用户
|
|
||||||
* @param request 请求
|
|
||||||
*/
|
|
||||||
public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
|
|
||||||
// 创建 Authentication,并设置到上下文
|
|
||||||
Authentication authentication = buildAuthentication(loginUser, request);
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
|
|
||||||
// 创建 UsernamePasswordAuthenticationToken 对象
|
|
||||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
|
|
||||||
loginUser, null, Collections.emptyList());
|
|
||||||
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
|
||||||
return authenticationToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package cn.iocoder.yudao.ssodemo.framework.core.util;
|
|
||||||
|
|
||||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
|
||||||
import cn.hutool.extra.servlet.ServletUtil;
|
|
||||||
import cn.hutool.json.JSONUtil;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端工具类
|
|
||||||
*
|
|
||||||
* @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 = JSONUtil.toJsonStr(object);
|
|
||||||
JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void write(HttpServletResponse response, String text, String contentType) {
|
|
||||||
JakartaServletUtil.write(response, text, contentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
server:
|
|
||||||
port: 18080
|
|
|
@ -1,154 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>首页</title>
|
|
||||||
<!-- jQuery:操作 dom、发起请求等 -->
|
|
||||||
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
|
|
||||||
|
|
||||||
<script type="application/javascript">
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 跳转单点登录
|
|
||||||
*/
|
|
||||||
function passwordLogin() {
|
|
||||||
window.location.href = '/login.html'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改昵称
|
|
||||||
*/
|
|
||||||
function updateNickname() {
|
|
||||||
const nickname = prompt("请输入新的昵称", "");
|
|
||||||
if (!nickname) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 更新用户的昵称
|
|
||||||
const accessToken = localStorage.getItem('ACCESS-TOKEN');
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:18080/user/update?nickname=" + nickname,
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + accessToken
|
|
||||||
},
|
|
||||||
success: function (result) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('更新昵称失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
alert('更新昵称成功!');
|
|
||||||
$('#nicknameSpan').html(nickname);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新令牌
|
|
||||||
*/
|
|
||||||
function refreshToken() {
|
|
||||||
const refreshToken = localStorage.getItem('REFRESH-TOKEN');
|
|
||||||
if (!refreshToken) {
|
|
||||||
alert("获取不到刷新令牌");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:18080/auth/refresh-token?refreshToken=" + refreshToken,
|
|
||||||
method: 'POST',
|
|
||||||
success: function (result) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('刷新访问令牌失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
alert('更新访问令牌成功!');
|
|
||||||
$('#accessTokenSpan').html(result.data.access_token);
|
|
||||||
|
|
||||||
// 设置到 localStorage 中
|
|
||||||
localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
|
|
||||||
localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 登出,删除访问令牌
|
|
||||||
*/
|
|
||||||
function logout() {
|
|
||||||
const accessToken = localStorage.getItem('ACCESS-TOKEN');
|
|
||||||
if (!accessToken) {
|
|
||||||
location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:18080/auth/logout",
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + accessToken
|
|
||||||
},
|
|
||||||
success: function (result) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('退出登录失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
alert('退出登录成功!');
|
|
||||||
// 删除 localStorage 中
|
|
||||||
localStorage.removeItem('ACCESS-TOKEN');
|
|
||||||
localStorage.removeItem('REFRESH-TOKEN');
|
|
||||||
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function () {
|
|
||||||
const accessToken = localStorage.getItem('ACCESS-TOKEN');
|
|
||||||
// 情况一:未登录
|
|
||||||
if (!accessToken) {
|
|
||||||
$('#noLoginDiv').css("display", "block");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 情况二:已登录
|
|
||||||
$('#yesLoginDiv').css("display", "block");
|
|
||||||
$('#accessTokenSpan').html(accessToken);
|
|
||||||
// 获得登录用户的信息
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:18080/user/get",
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + accessToken
|
|
||||||
},
|
|
||||||
success: function (result) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('获得个人信息失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$('#nicknameSpan').html(result.data.nickname);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- 情况一:未登录:1)跳转 ruoyi-vue-pro 的 SSO 登录页 -->
|
|
||||||
<div id="noLoginDiv" style="display: none">
|
|
||||||
您未登录,点击 <a href="#" onclick="passwordLogin()">跳转 </a> 账号密码登录
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 -->
|
|
||||||
<div id="yesLoginDiv" style="display: none">
|
|
||||||
您已登录!<button onclick="logout()">退出登录</button> <br />
|
|
||||||
昵称:<span id="nicknameSpan"> 加载中... </span> <button onclick="updateNickname()">修改昵称</button> <br />
|
|
||||||
访问令牌:<span id="accessTokenSpan"> 加载中... </span> <button onclick="refreshToken()">刷新令牌</button> <br />
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<style>
|
|
||||||
body { /** 页面居中 */
|
|
||||||
border-radius: 20px;
|
|
||||||
height: 350px;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%,-50%);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</html>
|
|
|
@ -1,74 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>登录</title>
|
|
||||||
<!-- jQuery:操作 dom、发起请求等 -->
|
|
||||||
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
|
|
||||||
|
|
||||||
<script type="application/javascript">
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 账号密码登录
|
|
||||||
*/
|
|
||||||
function login() {
|
|
||||||
const clientId = 'yudao-sso-demo-by-password'; // 可以改写成,你的 clientId
|
|
||||||
const clientSecret = 'test'; // 可以改写成,你的 clientSecret
|
|
||||||
const grantType = 'password'; // 密码模式
|
|
||||||
|
|
||||||
// 账号 + 密码
|
|
||||||
const username = $('#username').val();
|
|
||||||
const password = $('#password').val();
|
|
||||||
if (username.length === 0 || password.length === 0) {
|
|
||||||
alert('账号或密码未输入');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发起请求
|
|
||||||
$.ajax({
|
|
||||||
url: "http://127.0.0.1:48080/admin-api/system/oauth2/token?"
|
|
||||||
// 客户端
|
|
||||||
+ "client_id=" + clientId
|
|
||||||
+ "&client_secret=" + clientSecret
|
|
||||||
// 密码模式的参数
|
|
||||||
+ "&grant_type=" + grantType
|
|
||||||
+ "&username=" + username
|
|
||||||
+ "&password=" + password
|
|
||||||
+ '&scope=user.read user.write',
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'tenant-id': '1', // 多租户编号,写死
|
|
||||||
},
|
|
||||||
success: function (result) {
|
|
||||||
if (result.code !== 0) {
|
|
||||||
alert('登录失败,原因:' + result.msg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 设置到 localStorage 中
|
|
||||||
localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
|
|
||||||
localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
|
|
||||||
|
|
||||||
// 提示登录成功
|
|
||||||
alert('登录成功!点击确认,跳转回首页');
|
|
||||||
window.location.href = '/index.html';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
账号:<input id="username" value="admin" /> <br />
|
|
||||||
密码:<input id="password" value="admin123" > <br />
|
|
||||||
<button style="float: right; margin-top: 5px;" onclick="login()">登录</button>
|
|
||||||
</body>
|
|
||||||
<style>
|
|
||||||
body { /** 页面居中 */
|
|
||||||
border-radius: 20px;
|
|
||||||
height: 350px;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%,-50%);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</html>
|
|
|
@ -25,7 +25,6 @@
|
||||||
<module>yudao-spring-boot-starter-excel</module>
|
<module>yudao-spring-boot-starter-excel</module>
|
||||||
<module>yudao-spring-boot-starter-test</module>
|
<module>yudao-spring-boot-starter-test</module>
|
||||||
|
|
||||||
<module>yudao-spring-boot-starter-biz-operatelog</module>
|
|
||||||
<module>yudao-spring-boot-starter-biz-tenant</module>
|
<module>yudao-spring-boot-starter-biz-tenant</module>
|
||||||
<module>yudao-spring-boot-starter-biz-data-permission</module>
|
<module>yudao-spring-boot-starter-biz-data-permission</module>
|
||||||
<module>yudao-spring-boot-starter-biz-ip</module>
|
<module>yudao-spring-boot-starter-biz-ip</module>
|
||||||
|
|
|
@ -127,16 +127,17 @@
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.dromara.hutool</groupId>
|
|
||||||
<artifactId>hutool-all</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>transmittable-thread-local</artifactId>
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->
|
||||||
|
<artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因,方便 xxx-module-api 包使用 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Test 测试相关 -->
|
<!-- Test 测试相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package cn.iocoder.yudao.framework.common.enums;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间间隔的枚举
|
||||||
|
*
|
||||||
|
* @author dhb52
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum DateIntervalEnum implements IntArrayValuable {
|
||||||
|
|
||||||
|
DAY(1, "天"),
|
||||||
|
WEEK(2, "周"),
|
||||||
|
MONTH(3, "月"),
|
||||||
|
QUARTER(4, "季度"),
|
||||||
|
YEAR(5, "年")
|
||||||
|
;
|
||||||
|
|
||||||
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private final Integer interval;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateIntervalEnum valueOf(Integer interval) {
|
||||||
|
return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,74 +6,24 @@ import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstant
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ServiceException} 工具类
|
* {@link ServiceException} 工具类
|
||||||
*
|
*
|
||||||
* 目的在于,格式化异常信息提示。
|
* 目的在于,格式化异常信息提示。
|
||||||
* 考虑到 String.format 在参数不正确时会报错,因此使用 {} 作为占位符,并使用 {@link #doFormat(int, String, Object...)} 方法来格式化
|
* 考虑到 String.format 在参数不正确时会报错,因此使用 {} 作为占位符,并使用 {@link #doFormat(int, String, Object...)} 方法来格式化
|
||||||
*
|
*
|
||||||
* 因为 {@link #MESSAGES} 里面默认是没有异常信息提示的模板的,所以需要使用方自己初始化进去。目前想到的有几种方式:
|
|
||||||
*
|
|
||||||
* 1. 异常提示信息,写在枚举类中,例如说,cn.iocoder.oceans.user.api.constants.ErrorCodeEnum 类 + ServiceExceptionConfiguration
|
|
||||||
* 2. 异常提示信息,写在 .properties 等等配置文件
|
|
||||||
* 3. 异常提示信息,写在 Apollo 等等配置中心中,从而实现可动态刷新
|
|
||||||
* 4. 异常提示信息,存储在 db 等等数据库中,从而实现可动态刷新
|
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ServiceExceptionUtil {
|
public class ServiceExceptionUtil {
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误码提示模板
|
|
||||||
*/
|
|
||||||
private static final ConcurrentMap<Integer, String> MESSAGES = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public static void putAll(Map<Integer, String> messages) {
|
|
||||||
ServiceExceptionUtil.MESSAGES.putAll(messages);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void put(Integer code, String message) {
|
|
||||||
ServiceExceptionUtil.MESSAGES.put(code, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void delete(Integer code, String message) {
|
|
||||||
ServiceExceptionUtil.MESSAGES.remove(code, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== 和 ServiceException 的集成 ==========
|
// ========== 和 ServiceException 的集成 ==========
|
||||||
|
|
||||||
public static ServiceException exception(ErrorCode errorCode) {
|
public static ServiceException exception(ErrorCode errorCode) {
|
||||||
String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg());
|
return exception0(errorCode.getCode(), errorCode.getMsg());
|
||||||
return exception0(errorCode.getCode(), messagePattern);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ServiceException exception(ErrorCode errorCode, Object... params) {
|
public static ServiceException exception(ErrorCode errorCode, Object... params) {
|
||||||
String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg());
|
return exception0(errorCode.getCode(), errorCode.getMsg(), params);
|
||||||
return exception0(errorCode.getCode(), messagePattern, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建指定编号的 ServiceException 的异常
|
|
||||||
*
|
|
||||||
* @param code 编号
|
|
||||||
* @return 异常
|
|
||||||
*/
|
|
||||||
public static ServiceException exception(Integer code) {
|
|
||||||
return exception0(code, MESSAGES.get(code));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建指定编号的 ServiceException 的异常
|
|
||||||
*
|
|
||||||
* @param code 编号
|
|
||||||
* @param params 消息提示的占位符对应的参数
|
|
||||||
* @return 异常
|
|
||||||
*/
|
|
||||||
public static ServiceException exception(Integer code, Object... params) {
|
|
||||||
return exception0(code, MESSAGES.get(code), params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ServiceException exception0(Integer code, String messagePattern, Object... params) {
|
public static ServiceException exception0(Integer code, String messagePattern, Object... params) {
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
package cn.iocoder.yudao.framework.common.util.cache;
|
package cn.iocoder.yudao.framework.common.util.cache;
|
||||||
|
|
||||||
import com.alibaba.ttl.threadpool.TtlExecutors;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,14 +14,36 @@ import java.util.concurrent.Executors;
|
||||||
*/
|
*/
|
||||||
public class CacheUtils {
|
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) {
|
public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {
|
||||||
Executor executor = Executors.newCachedThreadPool( // TODO 芋艿:可能要思考下,未来要不要做成可配置
|
|
||||||
TtlExecutors.getDefaultDisableInheritableThreadFactory()); // TTL 保证 ThreadLocal 可以透传
|
|
||||||
return CacheBuilder.newBuilder()
|
return CacheBuilder.newBuilder()
|
||||||
// 只阻塞当前数据加载线程,其他线程返回旧值
|
// 只阻塞当前数据加载线程,其他线程返回旧值
|
||||||
.refreshAfterWrite(duration)
|
.refreshAfterWrite(duration)
|
||||||
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
|
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
|
||||||
.build(CacheLoader.asyncReloading(loader, executor));
|
.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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class CollectionUtils {
|
||||||
if (CollUtil.isEmpty(from)) {
|
if (CollUtil.isEmpty(from)) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
|
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,
|
public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from,
|
||||||
|
@ -87,7 +87,7 @@ public class CollectionUtils {
|
||||||
if (CollUtil.isEmpty(from)) {
|
if (CollUtil.isEmpty(from)) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
|
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) {
|
public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {
|
||||||
|
@ -97,6 +97,10 @@ public class CollectionUtils {
|
||||||
.collect(Collectors.toList());
|
.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) {
|
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
|
||||||
if (CollUtil.isEmpty(from)) {
|
if (CollUtil.isEmpty(from)) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
|
@ -123,7 +127,7 @@ public class CollectionUtils {
|
||||||
if (CollUtil.isEmpty(from)) {
|
if (CollUtil.isEmpty(from)) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
}
|
}
|
||||||
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
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,
|
public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from,
|
||||||
|
@ -132,7 +136,7 @@ public class CollectionUtils {
|
||||||
if (CollUtil.isEmpty(from)) {
|
if (CollUtil.isEmpty(from)) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
}
|
}
|
||||||
return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
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) {
|
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||||
|
@ -315,4 +319,4 @@ public class CollectionUtils {
|
||||||
return list.stream().flatMap(Collection::stream).collect(Collectors.toList());
|
return list.stream().flatMap(Collection::stream).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.collection;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
@ -40,6 +41,7 @@ public class MapUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从哈希表查找到 key 对应的 value,然后进一步处理
|
* 从哈希表查找到 key 对应的 value,然后进一步处理
|
||||||
|
* key 为 null 时, 不处理
|
||||||
* 注意,如果查找到的 value 为 null 时,不进行处理
|
* 注意,如果查找到的 value 为 null 时,不进行处理
|
||||||
*
|
*
|
||||||
* @param map 哈希表
|
* @param map 哈希表
|
||||||
|
@ -47,7 +49,7 @@ public class MapUtils {
|
||||||
* @param consumer 进一步处理的逻辑
|
* @param consumer 进一步处理的逻辑
|
||||||
*/
|
*/
|
||||||
public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
|
public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
|
||||||
if (CollUtil.isEmpty(map)) {
|
if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
V value = map.get(key);
|
V value = map.get(key);
|
||||||
|
|
|
@ -27,8 +27,6 @@ public class DateUtils {
|
||||||
|
|
||||||
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
|
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
public static final String FORMAT_HOUR_MINUTE_SECOND = "HH:mm:ss";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将 LocalDateTime 转换成 Date
|
* 将 LocalDateTime 转换成 Date
|
||||||
*
|
*
|
||||||
|
@ -67,19 +65,11 @@ public class DateUtils {
|
||||||
return new Date(System.currentTimeMillis() + duration.toMillis());
|
return new Date(System.currentTimeMillis() + duration.toMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isExpired(Date time) {
|
|
||||||
return System.currentTimeMillis() > time.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isExpired(LocalDateTime time) {
|
public static boolean isExpired(LocalDateTime time) {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
return now.isAfter(time);
|
return now.isAfter(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long diff(Date endTime, Date startTime) {
|
|
||||||
return endTime.getTime() - startTime.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建指定时间
|
* 创建指定时间
|
||||||
*
|
*
|
||||||
|
@ -136,37 +126,6 @@ public class DateUtils {
|
||||||
return a.isAfter(b) ? a : b;
|
return a.isAfter(b) ? a : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算当期时间相差的日期
|
|
||||||
*
|
|
||||||
* @param field 日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等.
|
|
||||||
* @param amount 相差的数值
|
|
||||||
* @return 计算后的日志
|
|
||||||
*/
|
|
||||||
public static Date addDate(int field, int amount) {
|
|
||||||
return addDate(null, field, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算当期时间相差的日期
|
|
||||||
*
|
|
||||||
* @param date 设置时间
|
|
||||||
* @param field 日历字段 例如说,{@link Calendar#DAY_OF_MONTH} 等
|
|
||||||
* @param amount 相差的数值
|
|
||||||
* @return 计算后的日志
|
|
||||||
*/
|
|
||||||
public static Date addDate(Date date, int field, int amount) {
|
|
||||||
if (amount == 0) {
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
Calendar c = Calendar.getInstance();
|
|
||||||
if (date != null) {
|
|
||||||
c.setTime(date);
|
|
||||||
}
|
|
||||||
c.add(field, amount);
|
|
||||||
return c.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否今天
|
* 是否今天
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
package cn.iocoder.yudao.framework.common.util.date;
|
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.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.Duration;
|
import java.time.*;
|
||||||
import java.time.LocalDate;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.LocalTime;
|
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.time.temporal.TemporalAdjusters;
|
import java.time.temporal.TemporalAdjusters;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 时间工具类,用于 {@link java.time.LocalDateTime}
|
* 时间工具类,用于 {@link java.time.LocalDateTime}
|
||||||
|
@ -21,6 +26,22 @@ public class LocalDateTimeUtils {
|
||||||
*/
|
*/
|
||||||
public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
|
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) {
|
public static LocalDateTime addTime(Duration duration) {
|
||||||
return LocalDateTime.now().plus(duration);
|
return LocalDateTime.now().plus(duration);
|
||||||
}
|
}
|
||||||
|
@ -54,6 +75,21 @@ public class LocalDateTimeUtils {
|
||||||
return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, 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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断当前时间是否在该时间范围内
|
* 判断当前时间是否在该时间范围内
|
||||||
*
|
*
|
||||||
|
@ -122,6 +158,16 @@ public class LocalDateTimeUtils {
|
||||||
return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
|
return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定日期所在季度
|
||||||
|
*
|
||||||
|
* @param date 日期
|
||||||
|
* @return 所在季度
|
||||||
|
*/
|
||||||
|
public static int getQuarterOfYear(LocalDateTime date) {
|
||||||
|
return (date.getMonthValue() - 1) / 3 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负
|
* 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负
|
||||||
*
|
*
|
||||||
|
@ -168,4 +214,96 @@ public class LocalDateTimeUtils {
|
||||||
return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,10 @@ public class NumberUtils {
|
||||||
return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null;
|
return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Integer parseInt(String str) {
|
||||||
|
return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过经纬度获取地球上两点之间的距离
|
* 通过经纬度获取地球上两点之间的距离
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package cn.iocoder.yudao.framework.common.util.servlet;
|
package cn.iocoder.yudao.framework.common.util.servlet;
|
||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
|
@ -12,8 +11,6 @@ import org.springframework.web.context.request.RequestAttributes;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,21 +32,6 @@ public class ServletUtils {
|
||||||
JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
JakartaServletUtil.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 请求
|
* @param request 请求
|
||||||
* @return ua
|
* @return ua
|
||||||
|
@ -93,11 +75,19 @@ public class ServletUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getBody(HttpServletRequest request) {
|
public static String getBody(HttpServletRequest request) {
|
||||||
return JakartaServletUtil.getBody(request);
|
// 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取
|
||||||
|
if (isJsonRequest(request)) {
|
||||||
|
return JakartaServletUtil.getBody(request);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getBodyBytes(HttpServletRequest request) {
|
public static byte[] getBodyBytes(HttpServletRequest request) {
|
||||||
return JakartaServletUtil.getBodyBytes(request);
|
// 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取
|
||||||
|
if (isJsonRequest(request)) {
|
||||||
|
return JakartaServletUtil.getBodyBytes(request);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getClientIP(HttpServletRequest request) {
|
public static String getClientIP(HttpServletRequest request) {
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.common.util.spring;
|
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
|
||||||
import org.springframework.aop.framework.AdvisedSupport;
|
|
||||||
import org.springframework.aop.framework.AopProxy;
|
|
||||||
import org.springframework.aop.support.AopUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring AOP 工具类
|
|
||||||
*
|
|
||||||
* 参考波克尔 http://www.bubuko.com/infodetail-3471885.html 实现
|
|
||||||
*/
|
|
||||||
public class SpringAopUtils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取代理的目标对象
|
|
||||||
*
|
|
||||||
* @param proxy 代理对象
|
|
||||||
* @return 目标对象
|
|
||||||
*/
|
|
||||||
public static Object getTarget(Object proxy) throws Exception {
|
|
||||||
// 不是代理对象
|
|
||||||
if (!AopUtils.isAopProxy(proxy)) {
|
|
||||||
return proxy;
|
|
||||||
}
|
|
||||||
// Jdk 代理
|
|
||||||
if (AopUtils.isJdkDynamicProxy(proxy)) {
|
|
||||||
return getJdkDynamicProxyTargetObject(proxy);
|
|
||||||
}
|
|
||||||
// Cglib 代理
|
|
||||||
return getCglibProxyTargetObject(proxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
|
|
||||||
Object dynamicAdvisedInterceptor = BeanUtil.getFieldValue(proxy, "CGLIB$CALLBACK_0");
|
|
||||||
AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(dynamicAdvisedInterceptor, "advised");
|
|
||||||
return advisedSupport.getTargetSource().getTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
|
|
||||||
AopProxy aopProxy = (AopProxy) BeanUtil.getFieldValue(proxy, "h");
|
|
||||||
AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(aopProxy, "advised");
|
|
||||||
return advisedSupport.getTargetSource().getTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
package cn.iocoder.yudao.framework.common.util.string;
|
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.ArrayUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,6 +47,15 @@ public class StrUtils {
|
||||||
return Arrays.stream(longs).boxed().collect(Collectors.toList());
|
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) {
|
public static List<Integer> splitToInteger(String value, CharSequence separator) {
|
||||||
int[] integers = StrUtil.splitToInt(value, separator);
|
int[] integers = StrUtil.splitToInt(value, separator);
|
||||||
return Arrays.stream(integers).boxed().collect(Collectors.toList());
|
return Arrays.stream(integers).boxed().collect(Collectors.toList());
|
||||||
|
|
|
@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||||
import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;
|
import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据权限 Util
|
* 数据权限 Util
|
||||||
*
|
*
|
||||||
|
@ -40,4 +42,22 @@ public class DataPermissionUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略数据权限,执行对应的逻辑
|
||||||
|
*
|
||||||
|
* @param callable 逻辑
|
||||||
|
* @return 执行结果
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static <T> T executeIgnore(Callable<T> callable) {
|
||||||
|
DataPermission dataPermission = getDisableDataPermissionDisable();
|
||||||
|
DataPermissionContextHolder.add(dataPermission);
|
||||||
|
try {
|
||||||
|
// 执行 callable
|
||||||
|
return callable.call();
|
||||||
|
} finally {
|
||||||
|
DataPermissionContextHolder.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -522,6 +522,29 @@ id,name,type,parentId
|
||||||
441931,凤岗镇,4,441900
|
441931,凤岗镇,4,441900
|
||||||
441932,长安镇,4,441900
|
441932,长安镇,4,441900
|
||||||
442000,中山市,3,440000
|
442000,中山市,3,440000
|
||||||
|
442001,石岐街道,4,442000
|
||||||
|
442002,东区街道,4,442000
|
||||||
|
442003,中山港街道,4,442000
|
||||||
|
442004,西区街道,4,442000
|
||||||
|
442005,南区街道,4,442000
|
||||||
|
442006,五桂山街道,4,442000
|
||||||
|
442007,民众街道,4,442000
|
||||||
|
442008,南朗街道,4,442000
|
||||||
|
442009,黄圃镇,4,442000
|
||||||
|
442010,东凤镇,4,442000
|
||||||
|
442011,古镇镇,4,442000
|
||||||
|
442012,沙溪镇,4,442000
|
||||||
|
442013,坦洲镇,4,442000
|
||||||
|
442014,港口镇,4,442000
|
||||||
|
442015,三角镇,4,442000
|
||||||
|
442016,横栏镇,4,442000
|
||||||
|
442017,南头镇,4,442000
|
||||||
|
442018,阜沙镇,4,442000
|
||||||
|
442019,三乡镇,4,442000
|
||||||
|
442020,板芙镇,4,442000
|
||||||
|
442021,大涌镇,4,442000
|
||||||
|
442022,神湾镇,4,442000
|
||||||
|
442023,小榄镇,4,442000
|
||||||
445100,潮州市,3,440000
|
445100,潮州市,3,440000
|
||||||
445200,揭阳市,3,440000
|
445200,揭阳市,3,440000
|
||||||
445300,云浮市,3,440000
|
445300,云浮市,3,440000
|
||||||
|
|
|
|
@ -1,52 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-framework</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
</parent>
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<name>${project.artifactId}</name>
|
|
||||||
<description>操作日志</description>
|
|
||||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-common</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Spring 核心 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-aop</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Web 相关 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 业务组件 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-module-system-api</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 工具类相关 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
<artifactId>guava</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -1,23 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.operatelog.config;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.operatelog.core.aop.OperateLogAspect;
|
|
||||||
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkService;
|
|
||||||
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkServiceImpl;
|
|
||||||
import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
|
|
||||||
@AutoConfiguration
|
|
||||||
public class YudaoOperateLogAutoConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public OperateLogAspect operateLogAspect() {
|
|
||||||
return new OperateLogAspect();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public OperateLogFrameworkService operateLogFrameworkService(OperateLogApi operateLogApi) {
|
|
||||||
return new OperateLogFrameworkServiceImpl(operateLogApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.operatelog.core.annotations;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作日志注解
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Target({ElementType.METHOD})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface OperateLog {
|
|
||||||
|
|
||||||
// ========== 模块字段 ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作模块
|
|
||||||
*
|
|
||||||
* 为空时,会尝试读取 {@link Tag#name()} 属性
|
|
||||||
*/
|
|
||||||
String module() default "";
|
|
||||||
/**
|
|
||||||
* 操作名
|
|
||||||
*
|
|
||||||
* 为空时,会尝试读取 {@link Operation#summary()} 属性
|
|
||||||
*/
|
|
||||||
String name() default "";
|
|
||||||
/**
|
|
||||||
* 操作分类
|
|
||||||
*
|
|
||||||
* 实际并不是数组,因为枚举不能设置 null 作为默认值
|
|
||||||
*/
|
|
||||||
OperateTypeEnum[] type() default {};
|
|
||||||
|
|
||||||
// ========== 开关字段 ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否记录操作日志
|
|
||||||
*/
|
|
||||||
boolean enable() default true;
|
|
||||||
/**
|
|
||||||
* 是否记录方法参数
|
|
||||||
*/
|
|
||||||
boolean logArgs() default true;
|
|
||||||
/**
|
|
||||||
* 是否记录方法结果的数据
|
|
||||||
*/
|
|
||||||
boolean logResultData() default true;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,375 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.operatelog.core.aop;
|
|
||||||
|
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
|
||||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
|
||||||
import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
|
|
||||||
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLog;
|
|
||||||
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkService;
|
|
||||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
|
||||||
import org.aspectj.lang.annotation.Around;
|
|
||||||
import org.aspectj.lang.annotation.Aspect;
|
|
||||||
import org.aspectj.lang.reflect.MethodSignature;
|
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.validation.BindingResult;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.SUCCESS;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 拦截使用 @OperateLog 注解,如果满足条件,则生成操作日志。
|
|
||||||
* 满足如下任一条件,则会进行记录:
|
|
||||||
* 1. 使用 @ApiOperation + 非 @GetMapping
|
|
||||||
* 2. 使用 @OperateLog 注解
|
|
||||||
* <p>
|
|
||||||
* 但是,如果声明 @OperateLog 注解时,将 enable 属性设置为 false 时,强制不记录。
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Aspect
|
|
||||||
@Slf4j
|
|
||||||
public class OperateLogAspect {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用于记录操作内容的上下文
|
|
||||||
*
|
|
||||||
* @see OperateLog#getContent()
|
|
||||||
*/
|
|
||||||
private static final ThreadLocal<String> CONTENT = new ThreadLocal<>();
|
|
||||||
/**
|
|
||||||
* 用于记录拓展字段的上下文
|
|
||||||
*
|
|
||||||
* @see OperateLog#getExts()
|
|
||||||
*/
|
|
||||||
private static final ThreadLocal<Map<String, Object>> EXTS = new ThreadLocal<>();
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private OperateLogFrameworkService operateLogFrameworkService;
|
|
||||||
|
|
||||||
@Around("@annotation(operation)")
|
|
||||||
public Object around(ProceedingJoinPoint joinPoint, Operation operation) throws Throwable {
|
|
||||||
// 可能也添加了 @ApiOperation 注解
|
|
||||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog = getMethodAnnotation(joinPoint,
|
|
||||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog.class);
|
|
||||||
return around0(joinPoint, operateLog, operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Around("!@annotation(io.swagger.v3.oas.annotations.Operation) && @annotation(operateLog)")
|
|
||||||
// 兼容处理,只添加 @OperateLog 注解的情况
|
|
||||||
public Object around(ProceedingJoinPoint joinPoint,
|
|
||||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog) throws Throwable {
|
|
||||||
return around0(joinPoint, operateLog, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object around0(ProceedingJoinPoint joinPoint,
|
|
||||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
|
||||||
Operation operation) throws Throwable {
|
|
||||||
// 目前,只有管理员,才记录操作日志!所以非管理员,直接调用,不进行记录
|
|
||||||
Integer userType = WebFrameworkUtils.getLoginUserType();
|
|
||||||
if (!Objects.equals(userType, UserTypeEnum.ADMIN.getValue())) {
|
|
||||||
return joinPoint.proceed();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录开始时间
|
|
||||||
LocalDateTime startTime = LocalDateTime.now();
|
|
||||||
try {
|
|
||||||
// 执行原有方法
|
|
||||||
Object result = joinPoint.proceed();
|
|
||||||
// 记录正常执行时的操作日志
|
|
||||||
this.log(joinPoint, operateLog, operation, startTime, result, null);
|
|
||||||
return result;
|
|
||||||
} catch (Throwable exception) {
|
|
||||||
this.log(joinPoint, operateLog, operation, startTime, null, exception);
|
|
||||||
throw exception;
|
|
||||||
} finally {
|
|
||||||
clearThreadLocal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setContent(String content) {
|
|
||||||
CONTENT.set(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addExt(String key, Object value) {
|
|
||||||
if (EXTS.get() == null) {
|
|
||||||
EXTS.set(new HashMap<>());
|
|
||||||
}
|
|
||||||
EXTS.get().put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void clearThreadLocal() {
|
|
||||||
CONTENT.remove();
|
|
||||||
EXTS.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void log(ProceedingJoinPoint joinPoint,
|
|
||||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
|
||||||
Operation operation,
|
|
||||||
LocalDateTime startTime, Object result, Throwable exception) {
|
|
||||||
try {
|
|
||||||
// 判断不记录的情况
|
|
||||||
if (!isLogEnable(joinPoint, operateLog)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 真正记录操作日志
|
|
||||||
this.log0(joinPoint, operateLog, operation, startTime, result, exception);
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
log.error("[log][记录操作日志时,发生异常,其中参数是 joinPoint({}) operateLog({}) apiOperation({}) result({}) exception({}) ]",
|
|
||||||
joinPoint, operateLog, operation, result, exception, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void log0(ProceedingJoinPoint joinPoint,
|
|
||||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
|
||||||
Operation operation,
|
|
||||||
LocalDateTime startTime, Object result, Throwable exception) {
|
|
||||||
OperateLog operateLogObj = new OperateLog();
|
|
||||||
// 补全通用字段
|
|
||||||
operateLogObj.setTraceId(TracerUtils.getTraceId());
|
|
||||||
operateLogObj.setStartTime(startTime);
|
|
||||||
// 补充用户信息
|
|
||||||
fillUserFields(operateLogObj);
|
|
||||||
// 补全模块信息
|
|
||||||
fillModuleFields(operateLogObj, joinPoint, operateLog, operation);
|
|
||||||
// 补全请求信息
|
|
||||||
fillRequestFields(operateLogObj);
|
|
||||||
// 补全方法信息
|
|
||||||
fillMethodFields(operateLogObj, joinPoint, operateLog, startTime, result, exception);
|
|
||||||
|
|
||||||
// 异步记录日志
|
|
||||||
operateLogFrameworkService.createOperateLog(operateLogObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void fillUserFields(OperateLog operateLogObj) {
|
|
||||||
operateLogObj.setUserId(WebFrameworkUtils.getLoginUserId());
|
|
||||||
operateLogObj.setUserType(WebFrameworkUtils.getLoginUserType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void fillModuleFields(OperateLog operateLogObj,
|
|
||||||
ProceedingJoinPoint joinPoint,
|
|
||||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
|
||||||
Operation operation) {
|
|
||||||
// module 属性
|
|
||||||
if (operateLog != null) {
|
|
||||||
operateLogObj.setModule(operateLog.module());
|
|
||||||
}
|
|
||||||
if (StrUtil.isEmpty(operateLogObj.getModule())) {
|
|
||||||
Tag tag = getClassAnnotation(joinPoint, Tag.class);
|
|
||||||
if (tag != null) {
|
|
||||||
// 优先读取 @Tag 的 name 属性
|
|
||||||
if (StrUtil.isNotEmpty(tag.name())) {
|
|
||||||
operateLogObj.setModule(tag.name());
|
|
||||||
}
|
|
||||||
// 没有的话,读取 @API 的 description 属性
|
|
||||||
if (StrUtil.isEmpty(operateLogObj.getModule()) && ArrayUtil.isNotEmpty(tag.description())) {
|
|
||||||
operateLogObj.setModule(tag.description());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// name 属性
|
|
||||||
if (operateLog != null) {
|
|
||||||
operateLogObj.setName(operateLog.name());
|
|
||||||
}
|
|
||||||
if (StrUtil.isEmpty(operateLogObj.getName()) && operation != null) {
|
|
||||||
operateLogObj.setName(operation.summary());
|
|
||||||
}
|
|
||||||
// type 属性
|
|
||||||
if (operateLog != null && ArrayUtil.isNotEmpty(operateLog.type())) {
|
|
||||||
operateLogObj.setType(operateLog.type()[0].getType());
|
|
||||||
}
|
|
||||||
if (operateLogObj.getType() == null) {
|
|
||||||
RequestMethod requestMethod = obtainFirstMatchRequestMethod(obtainRequestMethod(joinPoint));
|
|
||||||
OperateTypeEnum operateLogType = convertOperateLogType(requestMethod);
|
|
||||||
operateLogObj.setType(operateLogType != null ? operateLogType.getType() : null);
|
|
||||||
}
|
|
||||||
// content 和 exts 属性
|
|
||||||
operateLogObj.setContent(CONTENT.get());
|
|
||||||
operateLogObj.setExts(EXTS.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void fillRequestFields(OperateLog operateLogObj) {
|
|
||||||
// 获得 Request 对象
|
|
||||||
HttpServletRequest request = ServletUtils.getRequest();
|
|
||||||
if (request == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 补全请求信息
|
|
||||||
operateLogObj.setRequestMethod(request.getMethod());
|
|
||||||
operateLogObj.setRequestUrl(request.getRequestURI());
|
|
||||||
operateLogObj.setUserIp(ServletUtils.getClientIP(request));
|
|
||||||
operateLogObj.setUserAgent(ServletUtils.getUserAgent(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void fillMethodFields(OperateLog operateLogObj,
|
|
||||||
ProceedingJoinPoint joinPoint,
|
|
||||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
|
||||||
LocalDateTime startTime, Object result, Throwable exception) {
|
|
||||||
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
|
|
||||||
operateLogObj.setJavaMethod(methodSignature.toString());
|
|
||||||
if (operateLog == null || operateLog.logArgs()) {
|
|
||||||
operateLogObj.setJavaMethodArgs(obtainMethodArgs(joinPoint));
|
|
||||||
}
|
|
||||||
if (operateLog == null || operateLog.logResultData()) {
|
|
||||||
operateLogObj.setResultData(obtainResultData(result));
|
|
||||||
}
|
|
||||||
operateLogObj.setDuration((int) (LocalDateTimeUtil.between(startTime, LocalDateTime.now()).toMillis()));
|
|
||||||
// (正常)处理 resultCode 和 resultMsg 字段
|
|
||||||
if (result instanceof CommonResult) {
|
|
||||||
CommonResult<?> commonResult = (CommonResult<?>) result;
|
|
||||||
operateLogObj.setResultCode(commonResult.getCode());
|
|
||||||
operateLogObj.setResultMsg(commonResult.getMsg());
|
|
||||||
} else {
|
|
||||||
operateLogObj.setResultCode(SUCCESS.getCode());
|
|
||||||
}
|
|
||||||
// (异常)处理 resultCode 和 resultMsg 字段
|
|
||||||
if (exception != null) {
|
|
||||||
operateLogObj.setResultCode(INTERNAL_SERVER_ERROR.getCode());
|
|
||||||
operateLogObj.setResultMsg(ExceptionUtil.getRootCauseMessage(exception));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isLogEnable(ProceedingJoinPoint joinPoint,
|
|
||||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog) {
|
|
||||||
// 有 @OperateLog 注解的情况下
|
|
||||||
if (operateLog != null) {
|
|
||||||
return operateLog.enable();
|
|
||||||
}
|
|
||||||
// 没有 @ApiOperation 注解的情况下,只记录 POST、PUT、DELETE 的情况
|
|
||||||
return obtainFirstLogRequestMethod(obtainRequestMethod(joinPoint)) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RequestMethod obtainFirstLogRequestMethod(RequestMethod[] requestMethods) {
|
|
||||||
if (ArrayUtil.isEmpty(requestMethods)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return Arrays.stream(requestMethods).filter(requestMethod ->
|
|
||||||
requestMethod == RequestMethod.POST
|
|
||||||
|| requestMethod == RequestMethod.PUT
|
|
||||||
|| requestMethod == RequestMethod.DELETE)
|
|
||||||
.findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RequestMethod obtainFirstMatchRequestMethod(RequestMethod[] requestMethods) {
|
|
||||||
if (ArrayUtil.isEmpty(requestMethods)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// 优先,匹配最优的 POST、PUT、DELETE
|
|
||||||
RequestMethod result = obtainFirstLogRequestMethod(requestMethods);
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// 然后,匹配次优的 GET
|
|
||||||
result = Arrays.stream(requestMethods).filter(requestMethod -> requestMethod == RequestMethod.GET)
|
|
||||||
.findFirst().orElse(null);
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// 兜底,获得第一个
|
|
||||||
return requestMethods[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static OperateTypeEnum convertOperateLogType(RequestMethod requestMethod) {
|
|
||||||
if (requestMethod == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
switch (requestMethod) {
|
|
||||||
case GET:
|
|
||||||
return OperateTypeEnum.GET;
|
|
||||||
case POST:
|
|
||||||
return OperateTypeEnum.CREATE;
|
|
||||||
case PUT:
|
|
||||||
return OperateTypeEnum.UPDATE;
|
|
||||||
case DELETE:
|
|
||||||
return OperateTypeEnum.DELETE;
|
|
||||||
default:
|
|
||||||
return OperateTypeEnum.OTHER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RequestMethod[] obtainRequestMethod(ProceedingJoinPoint joinPoint) {
|
|
||||||
RequestMapping requestMapping = AnnotationUtils.getAnnotation( // 使用 Spring 的工具类,可以处理 @RequestMapping 别名注解
|
|
||||||
((MethodSignature) joinPoint.getSignature()).getMethod(), RequestMapping.class);
|
|
||||||
return requestMapping != null ? requestMapping.method() : new RequestMethod[]{};
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("SameParameterValue")
|
|
||||||
private static <T extends Annotation> T getMethodAnnotation(ProceedingJoinPoint joinPoint, Class<T> annotationClass) {
|
|
||||||
return ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(annotationClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("SameParameterValue")
|
|
||||||
private static <T extends Annotation> T getClassAnnotation(ProceedingJoinPoint joinPoint, Class<T> annotationClass) {
|
|
||||||
return ((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaringClass().getAnnotation(annotationClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String obtainMethodArgs(ProceedingJoinPoint joinPoint) {
|
|
||||||
// TODO 提升:参数脱敏和忽略
|
|
||||||
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
|
|
||||||
String[] argNames = methodSignature.getParameterNames();
|
|
||||||
Object[] argValues = joinPoint.getArgs();
|
|
||||||
// 拼接参数
|
|
||||||
Map<String, Object> args = Maps.newHashMapWithExpectedSize(argValues.length);
|
|
||||||
for (int i = 0; i < argNames.length; i++) {
|
|
||||||
String argName = argNames[i];
|
|
||||||
Object argValue = argValues[i];
|
|
||||||
// 被忽略时,标记为 ignore 字符串,避免和 null 混在一起
|
|
||||||
args.put(argName, !isIgnoreArgs(argValue) ? argValue : "[ignore]");
|
|
||||||
}
|
|
||||||
return JsonUtils.toJsonString(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String obtainResultData(Object result) {
|
|
||||||
// TODO 提升:结果脱敏和忽略
|
|
||||||
if (result instanceof CommonResult) {
|
|
||||||
result = ((CommonResult<?>) result).getData();
|
|
||||||
}
|
|
||||||
return JsonUtils.toJsonString(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isIgnoreArgs(Object object) {
|
|
||||||
Class<?> clazz = object.getClass();
|
|
||||||
// 处理数组的情况
|
|
||||||
if (clazz.isArray()) {
|
|
||||||
return IntStream.range(0, Array.getLength(object))
|
|
||||||
.anyMatch(index -> isIgnoreArgs(Array.get(object, index)));
|
|
||||||
}
|
|
||||||
// 递归,处理数组、Collection、Map 的情况
|
|
||||||
if (Collection.class.isAssignableFrom(clazz)) {
|
|
||||||
return ((Collection<?>) object).stream()
|
|
||||||
.anyMatch((Predicate<Object>) OperateLogAspect::isIgnoreArgs);
|
|
||||||
}
|
|
||||||
if (Map.class.isAssignableFrom(clazz)) {
|
|
||||||
return isIgnoreArgs(((Map<?, ?>) object).values());
|
|
||||||
}
|
|
||||||
// obj
|
|
||||||
return object instanceof MultipartFile
|
|
||||||
|| object instanceof HttpServletRequest
|
|
||||||
|| object instanceof HttpServletResponse
|
|
||||||
|| object instanceof BindingResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.operatelog.core;
|
|
|
@ -1,110 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.operatelog.core.service;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作日志
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class OperateLog {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 链路追踪编号
|
|
||||||
*/
|
|
||||||
private String traceId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户编号
|
|
||||||
*/
|
|
||||||
private Long userId;
|
|
||||||
/**
|
|
||||||
* 用户类型
|
|
||||||
*/
|
|
||||||
private Integer userType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作模块
|
|
||||||
*/
|
|
||||||
private String module;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作名
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作分类
|
|
||||||
*/
|
|
||||||
private Integer type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作明细
|
|
||||||
*/
|
|
||||||
private String content;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 拓展字段
|
|
||||||
*/
|
|
||||||
private Map<String, Object> exts;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 请求方法名
|
|
||||||
*/
|
|
||||||
private String requestMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 请求地址
|
|
||||||
*/
|
|
||||||
private String requestUrl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户 IP
|
|
||||||
*/
|
|
||||||
private String userIp;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 浏览器 UserAgent
|
|
||||||
*/
|
|
||||||
private String userAgent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Java 方法名
|
|
||||||
*/
|
|
||||||
private String javaMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Java 方法的参数
|
|
||||||
*/
|
|
||||||
private String javaMethodArgs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 开始时间
|
|
||||||
*/
|
|
||||||
private LocalDateTime startTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行时长,单位:毫秒
|
|
||||||
*/
|
|
||||||
private Integer duration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 结果码
|
|
||||||
*/
|
|
||||||
private Integer resultCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 结果提示
|
|
||||||
*/
|
|
||||||
private String resultMsg;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 结果数据
|
|
||||||
*/
|
|
||||||
private String resultData;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.operatelog.core.service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作日志 Framework Service 接口
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
public interface OperateLogFrameworkService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 记录操作日志
|
|
||||||
*
|
|
||||||
* @param operateLog 操作日志请求
|
|
||||||
*/
|
|
||||||
void createOperateLog(OperateLog operateLog);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.operatelog.core.service;
|
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
|
||||||
import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
|
|
||||||
import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.scheduling.annotation.Async;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作日志 Framework Service 实现类
|
|
||||||
*
|
|
||||||
* 基于 {@link OperateLogApi} 实现,记录操作日志
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class OperateLogFrameworkServiceImpl implements OperateLogFrameworkService {
|
|
||||||
|
|
||||||
private final OperateLogApi operateLogApi;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Async
|
|
||||||
public void createOperateLog(OperateLog operateLog) {
|
|
||||||
OperateLogCreateReqDTO reqDTO = BeanUtil.toBean(operateLog, OperateLogCreateReqDTO.class);
|
|
||||||
operateLogApi.createOperateLog(reqDTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.operatelog.core.util;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.operatelog.core.aop.OperateLogAspect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作日志工具类
|
|
||||||
* 目前主要的作用,是提供给业务代码,记录操作明细和拓展字段
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
public class OperateLogUtils {
|
|
||||||
|
|
||||||
public static void setContent(String content) {
|
|
||||||
OperateLogAspect.setContent(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addExt(String key, Object value) {
|
|
||||||
OperateLogAspect.addExt(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
/**
|
|
||||||
* 用户操作日志:记录用户的操作,用于对用户的操作的审计与追溯,永久保存。
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
package cn.iocoder.yudao.framework.operatelog;
|
|
|
@ -1 +0,0 @@
|
||||||
cn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogAutoConfiguration
|
|
|
@ -1,6 +1,5 @@
|
||||||
package cn.iocoder.yudao.framework.tenant.core.context;
|
package cn.iocoder.yudao.framework.tenant.core.context;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.DocumentEnum;
|
import cn.iocoder.yudao.framework.common.enums.DocumentEnum;
|
||||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||||
|
|
||||||
|
@ -30,16 +29,6 @@ public class TenantContextHolder {
|
||||||
return TENANT_ID.get();
|
return TENANT_ID.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得租户编号 String
|
|
||||||
*
|
|
||||||
* @return 租户编号
|
|
||||||
*/
|
|
||||||
public static String getTenantIdStr() {
|
|
||||||
Long tenantId = getTenantId();
|
|
||||||
return StrUtil.toStringOrNull(tenantId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得租户编号。如果不存在,则抛出 NullPointerException 异常
|
* 获得租户编号。如果不存在,则抛出 NullPointerException 异常
|
||||||
*
|
*
|
||||||
|
|
|
@ -11,6 +11,7 @@ import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字典工具类
|
* 字典工具类
|
||||||
|
@ -24,6 +25,7 @@ public class DictFrameworkUtils {
|
||||||
|
|
||||||
private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO();
|
private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO();
|
||||||
|
|
||||||
|
// TODO @puhui999:GET_DICT_DATA_CACHE、GET_DICT_DATA_LIST_CACHE、PARSE_DICT_DATA_CACHE 这 3 个缓存是有点重叠,可以思考下,有没可能减少 1 个。微信讨论好私聊,再具体改哈
|
||||||
/**
|
/**
|
||||||
* 针对 {@link #getDictDataLabel(String, String)} 的缓存
|
* 针对 {@link #getDictDataLabel(String, String)} 的缓存
|
||||||
*/
|
*/
|
||||||
|
@ -38,6 +40,20 @@ public class DictFrameworkUtils {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 针对 {@link #getDictDataLabelList(String)} 的缓存
|
||||||
|
*/
|
||||||
|
private static final LoadingCache<String, List<String>> GET_DICT_DATA_LIST_CACHE = CacheUtils.buildAsyncReloadingCache(
|
||||||
|
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
||||||
|
new CacheLoader<String, List<String>>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> load(String dictType) {
|
||||||
|
return dictDataApi.getDictDataLabelList(dictType);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 针对 {@link #parseDictDataValue(String, String)} 的缓存
|
* 针对 {@link #parseDictDataValue(String, String)} 的缓存
|
||||||
*/
|
*/
|
||||||
|
@ -67,6 +83,11 @@ public class DictFrameworkUtils {
|
||||||
return GET_DICT_DATA_CACHE.get(new KeyValue<>(dictType, value)).getLabel();
|
return GET_DICT_DATA_CACHE.get(new KeyValue<>(dictType, value)).getLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static List<String> getDictDataLabelList(String dictType) {
|
||||||
|
return GET_DICT_DATA_LIST_CACHE.get(dictType);
|
||||||
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static String parseDictDataValue(String dictType, String label) {
|
public static String parseDictDataValue(String dictType, String label) {
|
||||||
return PARSE_DICT_DATA_CACHE.get(new KeyValue<>(dictType, label)).getValue();
|
return PARSE_DICT_DATA_CACHE.get(new KeyValue<>(dictType, label)).getValue();
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package cn.iocoder.yudao.framework.excel.core.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给 Excel 列添加下拉选择数据
|
||||||
|
*
|
||||||
|
* 其中 {@link #dictType()} 和 {@link #functionName()} 二选一
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
public @interface ExcelColumnSelect {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 字典类型
|
||||||
|
*/
|
||||||
|
String dictType() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 获取下拉数据源的方法名称
|
||||||
|
*/
|
||||||
|
String functionName() default "";
|
||||||
|
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
package cn.iocoder.yudao.framework.excel.core.enums;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
// TODO @puhui999:列表有办法通过 field name 么?主要考虑一个点,可能导入模版的顺序可能会变
|
|
||||||
/**
|
|
||||||
* Excel 列名枚举
|
|
||||||
* 默认枚举 26 列列名如果有需求更多的列名请自行补充
|
|
||||||
*
|
|
||||||
* @author HUIHUI
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
|
||||||
public enum ExcelColumn {
|
|
||||||
|
|
||||||
A(0), B(1), C(2), D(3), E(4), F(5), G(6), H(7), I(8),
|
|
||||||
J(9), K(10), L(11), M(12), N(13), O(14), P(15), Q(16),
|
|
||||||
R(17), S(18), T(19), U(20), V(21), W(22), X(23), Y(24),
|
|
||||||
Z(25);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 列索引
|
|
||||||
*/
|
|
||||||
private final int colNum;
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package cn.iocoder.yudao.framework.excel.core.function;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excel 列下拉数据源获取接口
|
||||||
|
*
|
||||||
|
* 为什么不直接解析字典还搞个接口?考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容
|
||||||
|
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
public interface ExcelColumnSelectFunction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得方法名称
|
||||||
|
*
|
||||||
|
* @return 方法名称
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得列下拉数据源
|
||||||
|
*
|
||||||
|
* @return 下拉数据源
|
||||||
|
*/
|
||||||
|
List<String> getOptions();
|
||||||
|
|
||||||
|
}
|
|
@ -2,25 +2,38 @@ package cn.iocoder.yudao.framework.excel.core.handler;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import cn.hutool.poi.excel.ExcelUtil;
|
||||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||||
import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn;
|
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction;
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
||||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||||
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
|
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
|
||||||
import org.apache.poi.ss.usermodel.*;
|
import org.apache.poi.ss.usermodel.*;
|
||||||
import org.apache.poi.ss.util.CellRangeAddressList;
|
import org.apache.poi.ss.util.CellRangeAddressList;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于固定 sheet 实现下拉框
|
* 基于固定 sheet 实现下拉框
|
||||||
*
|
*
|
||||||
* @author HUIHUI
|
* @author HUIHUI
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class SelectSheetWriteHandler implements SheetWriteHandler {
|
public class SelectSheetWriteHandler implements SheetWriteHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,21 +49,56 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
||||||
|
|
||||||
private static final String DICT_SHEET_NAME = "字典sheet";
|
private static final String DICT_SHEET_NAME = "字典sheet";
|
||||||
|
|
||||||
// TODO @puhui999:Map<ExcelColumn, List<String>> 可以么?之前用 keyvalue 的原因,返回给前端,无法用 linkedhashmap,默认 key 会乱序
|
/**
|
||||||
private final List<KeyValue<ExcelColumn, List<String>>> selectMap;
|
* key: 列 value: 下拉数据源
|
||||||
|
*/
|
||||||
|
private final Map<Integer, List<String>> selectMap = new HashMap<>();
|
||||||
|
|
||||||
public SelectSheetWriteHandler(List<KeyValue<ExcelColumn, List<String>>> selectMap) {
|
public SelectSheetWriteHandler(Class<?> head) {
|
||||||
if (CollUtil.isEmpty(selectMap)) {
|
// 加载下拉数据获取接口
|
||||||
this.selectMap = null;
|
Map<String, ExcelColumnSelectFunction> beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectFunction.class);
|
||||||
|
if (MapUtil.isEmpty(beansMap)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 校验一下 key 是否唯一
|
|
||||||
Map<String, Long> nameCounts = selectMap.stream()
|
|
||||||
.collect(Collectors.groupingBy(item -> item.getKey().name(), Collectors.counting()));
|
|
||||||
Assert.isFalse(nameCounts.entrySet().stream().allMatch(entry -> entry.getValue() > 1), "下拉数据 key 重复请排查!!!");
|
|
||||||
|
|
||||||
selectMap.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错
|
// 解析下拉数据
|
||||||
this.selectMap = selectMap;
|
int colIndex = 0;
|
||||||
|
for (Field field : head.getDeclaredFields()) {
|
||||||
|
if (field.isAnnotationPresent(ExcelColumnSelect.class)) {
|
||||||
|
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
||||||
|
if (excelProperty != null && excelProperty.index() != -1) {
|
||||||
|
colIndex = excelProperty.index();
|
||||||
|
}
|
||||||
|
getSelectDataList(colIndex, field);
|
||||||
|
}
|
||||||
|
colIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得下拉数据,并添加到 {@link #selectMap} 中
|
||||||
|
*
|
||||||
|
* @param colIndex 列索引
|
||||||
|
* @param field 字段
|
||||||
|
*/
|
||||||
|
private void getSelectDataList(int colIndex, Field field) {
|
||||||
|
ExcelColumnSelect columnSelect = field.getAnnotation(ExcelColumnSelect.class);
|
||||||
|
String dictType = columnSelect.dictType();
|
||||||
|
String functionName = columnSelect.functionName();
|
||||||
|
Assert.isTrue(ObjectUtil.isNotEmpty(dictType) || ObjectUtil.isNotEmpty(functionName),
|
||||||
|
"Field({}) 的 @ExcelColumnSelect 注解,dictType 和 functionName 不能同时为空", field.getName());
|
||||||
|
|
||||||
|
// 情况一:使用 dictType 获得下拉数据
|
||||||
|
if (StrUtil.isNotEmpty(dictType)) { // 情况一: 字典数据 (默认)
|
||||||
|
selectMap.put(colIndex, DictFrameworkUtils.getDictDataLabelList(dictType));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 情况二:使用 functionName 获得下拉数据
|
||||||
|
Map<String, ExcelColumnSelectFunction> functionMap = SpringUtil.getApplicationContext().getBeansOfType(ExcelColumnSelectFunction.class);
|
||||||
|
ExcelColumnSelectFunction function = CollUtil.findOne(functionMap.values(), item -> item.getName().equals(functionName));
|
||||||
|
Assert.notNull(function, "未找到对应的 function({})", functionName);
|
||||||
|
selectMap.put(colIndex, function.getOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,18 +110,20 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
||||||
// 1. 获取相应操作对象
|
// 1. 获取相应操作对象
|
||||||
DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手
|
DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手
|
||||||
Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿
|
Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿
|
||||||
|
List<KeyValue<Integer, List<String>>> keyValues = convertList(selectMap.entrySet(), entry -> new KeyValue<>(entry.getKey(), entry.getValue()));
|
||||||
|
keyValues.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错
|
||||||
|
|
||||||
// 2. 创建数据字典的 sheet 页
|
// 2. 创建数据字典的 sheet 页
|
||||||
Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
|
Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
|
||||||
for (KeyValue<ExcelColumn, List<String>> keyValue : selectMap) {
|
for (KeyValue<Integer, List<String>> keyValue : keyValues) {
|
||||||
int rowLength = keyValue.getValue().size();
|
int rowLength = keyValue.getValue().size();
|
||||||
// 2.1 设置字典 sheet 页的值 每一列一个字典项
|
// 2.1 设置字典 sheet 页的值,每一列一个字典项
|
||||||
for (int i = 0; i < rowLength; i++) {
|
for (int i = 0; i < rowLength; i++) {
|
||||||
Row row = dictSheet.getRow(i);
|
Row row = dictSheet.getRow(i);
|
||||||
if (row == null) {
|
if (row == null) {
|
||||||
row = dictSheet.createRow(i);
|
row = dictSheet.createRow(i);
|
||||||
}
|
}
|
||||||
row.createCell(keyValue.getKey().getColNum()).setCellValue(keyValue.getValue().get(i));
|
row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i));
|
||||||
}
|
}
|
||||||
// 2.2 设置单元格下拉选择
|
// 2.2 设置单元格下拉选择
|
||||||
setColumnSelect(writeSheetHolder, workbook, helper, keyValue);
|
setColumnSelect(writeSheetHolder, workbook, helper, keyValue);
|
||||||
|
@ -84,10 +134,10 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
||||||
* 设置单元格下拉选择
|
* 设置单元格下拉选择
|
||||||
*/
|
*/
|
||||||
private static void setColumnSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper,
|
private static void setColumnSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper,
|
||||||
KeyValue<ExcelColumn, List<String>> keyValue) {
|
KeyValue<Integer, List<String>> keyValue) {
|
||||||
// 1.1 创建可被其他单元格引用的名称
|
// 1.1 创建可被其他单元格引用的名称
|
||||||
Name name = workbook.createName();
|
Name name = workbook.createName();
|
||||||
String excelColumn = keyValue.getKey().name();
|
String excelColumn = ExcelUtil.indexToColName(keyValue.getKey());
|
||||||
// 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2
|
// 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2
|
||||||
String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size();
|
String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size();
|
||||||
name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字
|
name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字
|
||||||
|
@ -97,7 +147,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
||||||
DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束
|
DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束
|
||||||
// 设置下拉单元格的首行、末行、首列、末列
|
// 设置下拉单元格的首行、末行、首列、末列
|
||||||
CellRangeAddressList rangeAddressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW,
|
CellRangeAddressList rangeAddressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW,
|
||||||
keyValue.getKey().getColNum(), keyValue.getKey().getColNum());
|
keyValue.getKey(), keyValue.getKey());
|
||||||
DataValidation validation = helper.createValidation(constraint, rangeAddressList);
|
DataValidation validation = helper.createValidation(constraint, rangeAddressList);
|
||||||
if (validation instanceof HSSFDataValidation) {
|
if (validation instanceof HSSFDataValidation) {
|
||||||
validation.setSuppressDropDownArrow(false);
|
validation.setSuppressDropDownArrow(false);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue