Conflicts:
	yudao-ui-admin/yarn.lock
This commit is contained in:
shizhong 2022-11-24 15:01:21 +08:00
commit f478448ec9
1021 changed files with 41282 additions and 24925 deletions

View File

@ -44,6 +44,18 @@
| `yudao-cloud` | Spring Cloud 微服务 | **[Gitee](https://gitee.com/zhijiantianya/yudao-cloud)**     [Github](https://github.com/YunaiV/yudao-cloud) |
| `Spring-Boot-Labs` | Spring Boot & Cloud 入门 | **[Gitee](https://gitee.com/zhijiantianya/SpringBoot-Labs)**     [Github](https://github.com/YunaiV/SpringBoot-Labs) |
## 😎 开源协议
**为什么推荐使用本项目?**
① 本项目采用比 Apache 2.0 更宽松的 [MIT License](https://gitee.com/zhijiantianya/ruoyi-vue-pro/blob/master/LICENSE) 开源协议,个人与企业可 100% 免费使用不用保留类作者、Copyright 信息。
② 代码全部开源,不会像其他项目一样,只开源部分代码,让你无法了解整个项目的架构设计。[国产开源项目对比](https://www.yuque.com/xiatian-bsgny/lm0ec1/wqf8mn)
![开源项目对比](https://static.iocoder.cn/project-vs.png?imageView2/2/format/webp/w/1280)
③ 代码整洁、架构整洁,遵循《阿里巴巴 Java 开发手册》规范代码注释详细57000 行 Java 代码22000 行代码注释。
## 🐼 内置功能
系统内置多种多种业务功能,可以用于快速你的业务系统:
@ -174,44 +186,44 @@ ps核心功能已经实现正在对接微信小程序中...
### 后端
| 框架 | 说明 | 版本 | 学习指南 |
|---------------------------------------------------------------------------------------------|-----------------------|------------------|----------------------------------------------------------------|
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.6.12 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.13-SNSAPSHOT | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 | |
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.17.7 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.3.20 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.6.5 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.2.3 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 6.7.2 | [文档](https://doc.iocoder.cn/bpm/) |
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
| [Knife4j](https://gitee.com/xiaoym/knife4j) | Swagger 增强 UI 实现 | 3.0.3 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
| [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 1.7.1 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) |
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 8.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 2.6.9 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.13.3 | |
| [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.5.2.Final | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.24 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.8.2 | - |
| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 4.8.0 | - |
| 框架 | 说明 | 版本 | 学习指南 |
|---------------------------------------------------------------------------------------------|-----------------------|-------------|----------------------------------------------------------------|
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.6.13 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.14 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 | |
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.17.7 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.3.23 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.6.8 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.2.3 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 6.7.2 | [文档](https://doc.iocoder.cn/bpm/) |
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
| [Knife4j](https://gitee.com/xiaoym/knife4j) | Swagger 增强 UI 实现 | 3.0.3 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
| [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 1.7.1 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) |
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 8.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 2.6.9 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.13.3 | |
| [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.5.3.Final | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.24 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.8.2 | - |
| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 4.8.0 | - |
### [管理后台 Vue2 前端](./yudao-ui-admin)
| 框架 | 说明 | 版本 |
|------------------------------------------------------------------------------|---------------|--------|
| [Vue](https://cn.vuejs.org/index.html) | JavaScript 框架 | 2.6.12 |
| [Vue Element Admin](https://panjiachen.github.io/vue-element-admin-site/zh/) | 后台前端解决方案 | - |
| 框架 | 说明 | 版本 |
|------------------------------------------------------------------------------|---------------|-------|
| [Vue](https://cn.vuejs.org/index.html) | JavaScript 框架 | 2.7.0 |
| [Vue Element Admin](https://panjiachen.github.io/vue-element-admin-site/zh/) | 后台前端解决方案 | - |
### [管理后台 Vue3 前端](./yudao-ui-admin-vue3)
| 框架 | 说明 | 版本 |
| 框架 | 说明 | 版本 |
|----------------------------------------------------------------------|-----------------|--------|
| [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.41 |
| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 3.1.8 |
| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.18 |
| [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.44 |
| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 3.2.3 |
| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.20 |
| [TypeScript](https://www.typescriptlang.org/docs/) | TypeScript | 4.8.4 |
| [pinia](https://pinia.vuejs.org/) | vuex5 | 2.0.23 |
| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.2.2 |

View File

@ -38,7 +38,7 @@
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
<!-- 看看咋放到 bom 里 -->
<lombok.version>1.18.24</lombok.version>
<mapstruct.version>1.5.2.Final</mapstruct.version>
<mapstruct.version>1.5.3.Final</mapstruct.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@ -1,6 +1,9 @@
-- ----------------------------
-- Table structure for system_menu
-- icon 不兼容
-- 注意只有在使用 yudao-ui-admin-vue3 才进行导入
-- 注意只有在使用 yudao-ui-admin-vue3 才进行导入
-- 注意只有在使用 yudao-ui-admin-vue3 才进行导入
-- ----------------------------
DROP TABLE IF EXISTS `system_menu`;
CREATE TABLE `system_menu` (
@ -257,7 +260,7 @@ INSERT INTO `system_menu` VALUES (1264, '客户端查询', 'system:oauth2-client
INSERT INTO `system_menu` VALUES (1265, '客户端创建', 'system:oauth2-client:create', 3, 2, 1263, '', '', '', 0, b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:23', b'0');
INSERT INTO `system_menu` VALUES (1266, '客户端更新', 'system:oauth2-client:update', 3, 3, 1263, '', '', '', 0, b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:28', b'0');
INSERT INTO `system_menu` VALUES (1267, '客户端删除', 'system:oauth2-client:delete', 3, 4, 1263, '', '', '', 0, b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:33', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1281, '可视化报表', '', 1, 12, 0, '/visualization', 'ep:histogram', NULL, 0, b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2022-07-10 20:33:30', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1282, '积木报表', '', 2, 1, 1281, 'jimu-report', 'ep:histogram', 'visualization/jmreport/index', 0, b'1', b'1', '1', '2022-07-10 20:26:36', '1', '2022-07-28 21:17:34', b'0');
INSERT INTO `system_menu` VALUES (1281, '可视化报表', '', 1, 12, 0, '/visualization', 'ep:histogram', NULL, 0, b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2022-07-10 20:33:30', b'0');
INSERT INTO `system_menu` VALUES (1282, '积木报表', '', 2, 1, 1281, 'jimu-report', 'ep:histogram', 'visualization/jmreport/index', 0, b'1', b'1', '1', '2022-07-10 20:26:36', '1', '2022-07-28 21:17:34', b'0');
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -1,7 +0,0 @@
-- ----------------------------
-- 升级SQL文件全新安装只需要执行ruoyi-vue-pro.sql文件即可
-- 1.6.2 ==> 1.6.3
-- ----------------------------
-- 积木报表菜单
INSERT INTO `system_menu` VALUES (1281, '可视化报表', '', 1, 12, 0, '/visualization', 'chart', NULL, 0, b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2022-07-10 20:33:30', b'0');
INSERT INTO `system_menu` VALUES (1282, '积木报表', '', 2, 1, 1281, 'jm-report', '#', 'visualization/jm/index', 0, b'1', b'1', '1', '2022-07-10 20:26:36', '1', '2022-07-10 20:33:26', b'0');

View File

@ -20,10 +20,10 @@ CREATE TABLE `coupon`
`whether_noticed` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否已提醒',
`state` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '优惠券状态 1已领用未使用 2已使用 3已过期',
`get_type` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取',
`fetch_time` datetime NOT NULL DEFAULT 0 COMMENT '领取时间',
`use_time` datetime NOT NULL DEFAULT 0 COMMENT '使用时间',
`start_time` datetime NOT NULL DEFAULT 0 COMMENT '可使用的开始时间',
`end_time` datetime NOT NULL DEFAULT 0 COMMENT '有效期结束时间',
`fetch_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '领取时间',
`use_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '使用时间',
`start_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '可使用的开始时间',
`end_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '有效期结束时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',

View File

@ -23,18 +23,18 @@ SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `market_activity`;
CREATE TABLE `market_activity` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '活动编号',
`title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '活动标题',
`title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '活动标题',
`activity_type` tinyint NOT NULL COMMENT '活动类型',
`status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`invalid_time` datetime NULL DEFAULT NULL COMMENT '失效时间',
`delete_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
`time_limited_discount` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串使用 JSON 序列化成字符串存储',
`full_privilege` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串使用 JSON 序列化成字符串存储',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
`time_limited_discount` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '限制折扣字符串使用 JSON 序列化成字符串存储',
`full_privilege` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '限制折扣字符串使用 JSON 序列化成字符串存储',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
@ -53,18 +53,18 @@ COMMIT;
DROP TABLE IF EXISTS `market_banner`;
CREATE TABLE `market_banner` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Banner编号',
`title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'Banner标题',
`pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '图片URL',
`title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT 'Banner标题',
`pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '图片URL',
`status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '跳转地址',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '跳转地址',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
`sort` tinyint NULL DEFAULT NULL COMMENT '排序',
`memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '描述',
`memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Banner管理';
@ -81,11 +81,11 @@ DROP TABLE IF EXISTS `member_address`;
CREATE TABLE `member_address` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '收件地址编号',
`user_id` bigint NOT NULL COMMENT '用户编号',
`name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件人名称',
`mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '手机号',
`name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收件人名称',
`mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '手机号',
`area_id` bigint NOT NULL COMMENT '地区编码',
`post_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '邮编',
`detail_address` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件详细地址',
`post_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮编',
`detail_address` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收件详细地址',
`defaulted` bit(1) NOT NULL COMMENT '是否默认',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@ -95,7 +95,7 @@ CREATE TABLE `member_address` (
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_userId`(`user_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '用户收件地址';
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户收件地址';
-- ----------------------------
-- Records of member_address
@ -248,7 +248,7 @@ DROP TABLE IF EXISTS `product_spu`;
CREATE TABLE `product_spu` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
`brand_id` int DEFAULT NULL COMMENT '商品品牌编号',
`brand_id` bigint DEFAULT NULL COMMENT '商品品牌编号',
`category_id` bigint NOT NULL COMMENT '分类id',
`spec_type` int NOT NULL COMMENT '规格类型0 单规格 1 多规格',
`code` varchar(128) DEFAULT NULL COMMENT '商品编码',
@ -307,7 +307,7 @@ INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `
INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2022, '规格更新', 'product:property:update', 3, 3, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2023, '规格删除', 'product:property:delete', 3, 4, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2024, '规格导出', 'product:property:export', 3, 5, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2025, 'Banner管理', '', 2, 1, 2000, 'brand', '', 'mall/market/banner/index', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2025, 'Banner管理', '', 2, 1, 2000, 'banner', '', 'mall/market/banner/index', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2026, 'Banner查询', 'market:banner:query', 3, 1, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 'Banner创建', 'market:banner:create', 3, 2, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 'Banner更新', 'market:banner:update', 3, 3, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');

View File

@ -1,4 +1,5 @@
/**todo cancelType 设置默认值 0?*/
DROP TABLE IF EXISTS `trade_order`;
CREATE TABLE `trade_order`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
@ -10,9 +11,10 @@ CREATE TABLE `trade_order`
`user_remark` varchar(200) DEFAULT NULL COMMENT '用户备注',
`status` int NOT NULL DEFAULT '0' COMMENT '订单状态[0:待付款 1:待发货 2:待收货 3:已完成 4:已关闭]',
`product_count` int NOT NULL COMMENT '购买的商品数量',
`cancel_type` int NOT NULL COMMENT '取消类型[10:超时未支付 20:退款关闭 30:买家取消 40:已通过货到付款交易]',
`cancel_type` int DEFAULT NULL COMMENT '取消类型[10:超时未支付 20:退款关闭 30:买家取消 40:已通过货到付款交易]',
`remark` varchar(200) DEFAULT NULL COMMENT '商家备注',
`payed` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已支付[0:未支付 1:已经支付过]',
`pay_time` datetime DEFAULT NULL COMMENT '订单支付时间',
`finish_time` datetime DEFAULT NULL COMMENT '订单完成时间',
`cancel_time` datetime DEFAULT NULL COMMENT '订单取消时间',
`sku_original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价单位',
@ -20,11 +22,11 @@ CREATE TABLE `trade_order`
`order_promotion_price` int NOT NULL DEFAULT '0' COMMENT '订单优惠单位',
`delivery_price` int NOT NULL DEFAULT '0' COMMENT '运费金额单位',
`pay_price` int NOT NULL DEFAULT '0' COMMENT '应付金额单位',
`pay_order_id` int NOT NULL COMMENT '支付订单编号',
`pay_channel` int NOT NULL COMMENT '支付成功的支付渠道',
`delivery_type` int NOT NULL DEFAULT '1' COMMENT '配送方式:[1:快递发货 2:自提]',
`actual_delivery_type` int NOT NULL DEFAULT '1' COMMENT '实际的配送方式:[1:快递发货 2:自提]',
`delivery_templateid` int DEFAULT NULL COMMENT '配置模板的编号',
`pay_order_id` int DEFAULT NULL COMMENT '支付订单编号',
`pay_channel` int DEFAULT NULL COMMENT '支付成功的支付渠道',
`delivery_type` int DEFAULT NULL DEFAULT '1' COMMENT '配送方式:[1:快递发货 2:自提]',
`actual_delivery_type` int DEFAULT NULL DEFAULT '1' COMMENT '实际的配送方式:[1:快递发货 2:自提]',
`delivery_template_id` int DEFAULT NULL COMMENT '配置模板的编号',
`express_no` int DEFAULT NULL COMMENT '物流公司单号',
`delivery_status` bit(1) NOT NULL DEFAULT b'0' COMMENT '发货状态[0:未发货 1:已发货]',
`delivery_time` datetime DEFAULT NULL COMMENT '发货时间',
@ -73,4 +75,4 @@ CREATE TABLE `trade_order_item`
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB COMMENT ='交易订单明细表';
) ENGINE = InnoDB COMMENT ='交易订单明细表';

View File

@ -16,19 +16,18 @@
<properties>
<revision>1.6.4-snapshot</revision>
<!-- 统一依赖管理 -->
<spring.boot.version>2.6.12</spring.boot.version>
<spring.boot.version>2.6.13</spring.boot.version>
<!-- Web 相关 -->
<knife4j.version>3.0.3</knife4j.version>
<swagger-annotations.version>1.6.7</swagger-annotations.version>
<servlet.versoin>2.5</servlet.versoin>
<!-- DB 相关 -->
<druid.version>1.2.13-SNSAPSHOT</druid.version>
<druid.version>1.2.14</druid.version>
<mybatis-plus.version>3.5.2</mybatis-plus.version>
<mybatis-plus-generator.version>3.5.2</mybatis-plus-generator.version>
<dynamic-datasource.version>3.5.2</dynamic-datasource.version>
<redisson.version>3.17.7</redisson.version>
<!-- Config 配置中心相关 -->
<apollo.version>2.0.1</apollo.version>
<!-- 服务保障相关 -->
<lock4j.version>2.2.2</lock4j.version>
<resilience4j.version>1.7.1</resilience4j.version>
@ -43,11 +42,10 @@
<!-- Bpm 工作流相关 -->
<flowable.version>6.7.2</flowable.version>
<!-- 工具类相关 -->
<jasypt-spring-boot-starter.version>3.0.4</jasypt-spring-boot-starter.version>
<lombok.version>1.18.24</lombok.version>
<mapstruct.version>1.5.2.Final</mapstruct.version>
<hutool.version>5.8.8</hutool.version>
<easyexcel.verion>3.1.1</easyexcel.verion>
<mapstruct.version>1.5.3.Final</mapstruct.version>
<hutool.version>5.8.9</hutool.version>
<easyexcel.verion>3.1.2</easyexcel.verion>
<velocity.version>2.3</velocity.version>
<screw.version>1.0.5</screw.version>
<fastjson.version>1.2.83</fastjson.version>
@ -225,17 +223,6 @@
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-config</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId> <!-- 引入 Apollo Client 库,实现内嵌的配置中心 -->
<version>${apollo.version}</version>
</dependency>
<!-- Job 定时任务相关 -->
<dependency>
@ -407,12 +394,6 @@
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId> <!-- 加解密 -->
<version>${jasypt-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>

View File

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

View File

@ -11,7 +11,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import javax.annotation.Resource;
@Configuration
@Configuration(proxyBeanMethods = false)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Resource

View File

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

View File

@ -11,7 +11,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import javax.annotation.Resource;
@Configuration
@Configuration(proxyBeanMethods = false)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Resource

View File

@ -20,7 +20,6 @@
<module>yudao-spring-boot-starter-file</module>
<module>yudao-spring-boot-starter-monitor</module>
<module>yudao-spring-boot-starter-protection</module>
<module>yudao-spring-boot-starter-config</module>
<module>yudao-spring-boot-starter-job</module>
<module>yudao-spring-boot-starter-mq</module>

View File

@ -105,6 +105,11 @@
<artifactId>jackson-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>

View File

@ -12,7 +12,8 @@ import lombok.Getter;
@AllArgsConstructor
public enum DocumentEnum {
REDIS_INSTALL("https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ", "Redis 安装文档");
REDIS_INSTALL("https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ", "Redis 安装文档"),
TENANT("https://doc.iocoder.cn", "SaaS 多租户文档");
private final String url;
private final String memo;

View File

@ -29,6 +29,7 @@ public interface GlobalErrorCodeConstants {
// ========== 服务端错误段 ==========
ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启");
// ========== 自定义错误段 ==========
ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求

View File

@ -1,8 +1,8 @@
package cn.iocoder.yudao.framework.common.util.date;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.time.Duration;
import java.time.*;
import java.util.Calendar;
import java.util.Date;
@ -25,6 +25,35 @@ public class DateUtils {
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
/**
* LocalDateTime 转换成 Date
*
* @param date LocalDateTime
* @return LocalDateTime
*/
public static Date of(LocalDateTime date) {
// 将此日期时间与时区相结合以创建 ZonedDateTime
ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
// 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
Instant instant = zonedDateTime.toInstant();
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
return Date.from(instant);
}
/**
* Date 转换成 LocalDateTime
*
* @param date Date
* @return LocalDateTime
*/
public static LocalDateTime of(Date date) {
// 转为时间戳
Instant instant = date.toInstant();
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
@Deprecated
public static Date addTime(Duration duration) {
return new Date(System.currentTimeMillis() + duration.toMillis());
}
@ -33,6 +62,11 @@ public class DateUtils {
return System.currentTimeMillis() > time.getTime();
}
public static boolean isExpired(LocalDateTime time) {
LocalDateTime now = LocalDateTime.now();
return now.isAfter(time);
}
public static long diff(Date endTime, Date startTime) {
return endTime.getTime() - startTime.getTime();
}
@ -40,24 +74,29 @@ public class DateUtils {
/**
* 创建指定时间
*
* @param year
* @param mouth
* @param day
* @param year
* @param mouth
* @param day
* @return 指定时间
*/
public static Date buildTime(int year, int mouth, int day) {
return buildTime(year, mouth, day, 0, 0, 0);
}
@Deprecated
public static LocalDateTime buildLocalDateTime(int year, int mouth, int day) {
return LocalDateTime.of(year, mouth, day, 0, 0, 0);
}
/**
* 创建指定时间
*
* @param year
* @param mouth
* @param day
* @param hour 小时
* @param minute 分钟
* @param second
* @param year
* @param mouth
* @param day
* @param hour 小时
* @param minute 分钟
* @param second
* @return 指定时间
*/
public static Date buildTime(int year, int mouth, int day,
@ -83,12 +122,19 @@ public class DateUtils {
return a.compareTo(b) > 0 ? a : b;
}
public static boolean beforeNow(Date date) {
return date.getTime() < System.currentTimeMillis();
public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {
if (a == null) {
return b;
}
if (b == null) {
return a;
}
return a.isAfter(b) ? a : b;
}
public static boolean afterNow(Date date) {
return date.getTime() >= System.currentTimeMillis();
@Deprecated
public static boolean afterNow(LocalDateTime localDateTime) {
return localDateTime.isAfter(LocalDateTime.now());
}
/**
@ -128,11 +174,8 @@ public class DateUtils {
* @param date 日期
* @return 是否
*/
public static boolean isToday(Date date) {
if (date == null) {
return false;
}
return DateUtil.isSameDay(date, new Date());
public static boolean isToday(LocalDateTime date) {
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
}
}

View File

@ -0,0 +1,53 @@
package cn.iocoder.yudao.framework.common.util.date;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.time.Duration;
import java.time.LocalDateTime;
/**
* 时间工具类用于 {@link java.time.LocalDateTime}
*
* @author 芋道源码
*/
public class LocalDateTimeUtils {
public static LocalDateTime addTime(Duration duration) {
return LocalDateTime.now().plus(duration);
}
public static boolean beforeNow(LocalDateTime date) {
return date.isBefore(LocalDateTime.now());
}
public static boolean afterNow(LocalDateTime date) {
return date.isAfter(LocalDateTime.now());
}
/**
* 创建指定时间
*
* @param year
* @param mouth
* @param day
* @return 指定时间
*/
public static LocalDateTime buildTime(int year, int mouth, int day) {
return LocalDateTime.of(year, mouth, day, 0, 0, 0);
}
/**
* 判断当前时间是否在该时间范围内
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 是否
*/
public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) {
if (startTime == null || endTime == null) {
return false;
}
return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
}
}

View File

@ -7,6 +7,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
@ -28,6 +29,7 @@ public class JsonUtils {
static {
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
}
/**

View File

@ -3,7 +3,10 @@ package cn.iocoder.yudao.framework.common.util.string;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* 字符串工具类
@ -37,4 +40,9 @@ public class StrUtils {
return false;
}
public static List<Long> splitToLong(String value, CharSequence separator) {
long[] longs = StrUtil.splitToLong(value, separator);
return Arrays.stream(longs).boxed().collect(Collectors.toList());
}
}

View File

@ -9,7 +9,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
public class YudaoBannerAutoConfiguration {
@Bean

View File

@ -17,7 +17,7 @@ import java.util.List;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
public class YudaoDataPermissionAutoConfiguration {
@Bean
@ -27,9 +27,8 @@ public class YudaoDataPermissionAutoConfiguration {
@Bean
public DataPermissionDatabaseInterceptor dataPermissionDatabaseInterceptor(MybatisPlusInterceptor interceptor,
List<DataPermissionRule> rules) {
DataPermissionRuleFactory ruleFactory) {
// 创建 DataPermissionDatabaseInterceptor 拦截器
DataPermissionRuleFactory ruleFactory = dataPermissionRuleFactory(rules);
DataPermissionDatabaseInterceptor inner = new DataPermissionDatabaseInterceptor(ruleFactory);
// 添加到 interceptor
// 需要加在首个主要是为了在分页插件前面这个是 MyBatis Plus 的规定

View File

@ -16,7 +16,7 @@ import java.util.List;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(LoginUser.class)
@ConditionalOnBean(value = {PermissionApi.class, DeptDataPermissionRuleCustomizer.class})
public class YudaoDeptDataPermissionAutoConfiguration {

View File

@ -87,7 +87,7 @@ public class DataPermissionDatabaseInterceptorTest extends BaseMockitoUnitTest {
interceptor.beforeQuery(null, mappedStatement, null, null, null, boundSql);
// 断言
verify(mpBs, times(1)).sql(
eq("SELECT * FROM t_user WHERE id = 1 AND dept_id = 100"));
eq("SELECT * FROM t_user WHERE id = 1 AND t_user.dept_id = 100"));
// 断言缓存
assertTrue(interceptor.getMappedStatementCache().getNoRewritableMappedStatements().isEmpty());
}

View File

@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Configuration(proxyBeanMethods = false)
public class YudaoDictAutoConfiguration {
@Bean

View File

@ -17,7 +17,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "yudao.error-code", value = "enable", matchIfMissing = true) // 允许使用 yudao.error-code.enable=false 禁用访问日志
@EnableConfigurationProperties(ErrorCodeProperties.class)
@EnableScheduling // 开启调度任务的功能因为 ErrorCodeRemoteLoader 通过定时刷新错误码

View File

@ -10,7 +10,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
/**
@ -41,7 +41,7 @@ public class ErrorCodeLoaderImpl implements ErrorCodeLoader {
/**
* 缓存错误码的最大更新时间用于后续的增量轮询判断是否有更新
*/
private Date maxUpdateTime;
private LocalDateTime maxUpdateTime;
@EventListener(ApplicationReadyEvent.class)
public void loadErrorCodes() {

View File

@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Configuration(proxyBeanMethods = false)
public class YudaoOperateLogAutoConfiguration {
@Bean

View File

@ -1,5 +1,6 @@
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;
@ -32,6 +33,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.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;
@ -94,7 +96,7 @@ public class OperateLogAspect {
}
// 记录开始时间
Date startTime = new Date();
LocalDateTime startTime = LocalDateTime.now();
try {
// 执行原有方法
Object result = joinPoint.proceed();
@ -128,7 +130,7 @@ public class OperateLogAspect {
private void log(ProceedingJoinPoint joinPoint,
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
ApiOperation apiOperation,
Date startTime, Object result, Throwable exception) {
LocalDateTime startTime, Object result, Throwable exception) {
try {
// 判断不记录的情况
if (!isLogEnable(joinPoint, operateLog)) {
@ -145,7 +147,7 @@ public class OperateLogAspect {
private void log0(ProceedingJoinPoint joinPoint,
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
ApiOperation apiOperation,
Date startTime, Object result, Throwable exception) {
LocalDateTime startTime, Object result, Throwable exception) {
OperateLog operateLogObj = new OperateLog();
// 补全通用字段
operateLogObj.setTraceId(TracerUtils.getTraceId());
@ -226,7 +228,7 @@ public class OperateLogAspect {
private static void fillMethodFields(OperateLog operateLogObj,
ProceedingJoinPoint joinPoint,
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
Date startTime, Object result, Throwable exception) {
LocalDateTime startTime, Object result, Throwable exception) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
operateLogObj.setJavaMethod(methodSignature.toString());
if (operateLog == null || operateLog.logArgs()) {
@ -235,7 +237,7 @@ public class OperateLogAspect {
if (operateLog == null || operateLog.logResultData()) {
operateLogObj.setResultData(obtainResultData(result));
}
operateLogObj.setDuration((int) (System.currentTimeMillis() - startTime.getTime()));
operateLogObj.setDuration((int) (LocalDateTimeUtil.between(startTime, LocalDateTime.now()).toMillis()));
// 正常处理 resultCode resultMsg 字段
if (result instanceof CommonResult) {
CommonResult<?> commonResult = (CommonResult<?>) result;

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.operatelog.core.service;
import lombok.Data;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.Map;
/**
@ -85,7 +85,7 @@ public class OperateLog {
/**
* 开始时间
*/
private Date startTime;
private LocalDateTime startTime;
/**
* 执行时长单位毫秒

View File

@ -11,7 +11,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PayProperties.class)
public class YudaoPayAutoConfiguration {

View File

@ -5,7 +5,7 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 支付通知 Response DTO
@ -33,7 +33,7 @@ public class PayOrderNotifyRespDTO {
/**
* 支付成功时间
*/
private Date successTime;
private LocalDateTime successTime;
/**
* 通知的原始数据

View File

@ -7,7 +7,7 @@ import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.Map;
/**
@ -68,7 +68,7 @@ public class PayOrderUnifiedReqDTO {
* 支付过期时间
*/
@NotNull(message = "支付过期时间不能为空")
private Date expireTime;
private LocalDateTime expireTime;
// ========== 拓展参数 ==========
/**

View File

@ -5,7 +5,7 @@ import lombok.Builder;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 从渠道返回数据中解析得到的支付退款通知的Notify DTO
@ -57,7 +57,7 @@ public class PayRefundNotifyDTO {
/**
* 退款成功时间
*/
private Date refundSuccessTime;
private LocalDateTime refundSuccessTime;
}

View File

@ -1,8 +1,7 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
import cn.iocoder.yudao.framework.pay.core.client.dto.*;
@ -61,7 +60,7 @@ public abstract class AbstractAlipayClient extends AbstractPayClient<AlipayPayCl
return PayOrderNotifyRespDTO.builder().orderExtensionNo(params.get("out_trade_no"))
.channelOrderNo(params.get("trade_no")).channelUserId(params.get("seller_id"))
.tradeStatus(params.get("trade_status"))
.successTime(DateUtil.parse(params.get("notify_time"), "yyyy-MM-dd HH:mm:ss"))
.successTime(LocalDateTimeUtil.parse(params.get("notify_time"), "yyyy-MM-dd HH:mm:ss"))
.data(data.getBody()).build();
}
@ -72,7 +71,7 @@ public abstract class AbstractAlipayClient extends AbstractPayClient<AlipayPayCl
.tradeNo(params.get("out_trade_no"))
.reqNo(params.get("out_biz_no"))
.status(PayNotifyRefundStatusEnum.SUCCESS)
.refundSuccessTime(DateUtil.parse(params.get("gmt_refund"), "yyyy-MM-dd HH:mm:ss"))
.refundSuccessTime(LocalDateTimeUtil.parse(params.get("gmt_refund"), "yyyy-MM-dd HH:mm:ss"))
.build();
return notifyDTO;
}

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
@ -49,7 +50,7 @@ public class WXLitePayClient extends AbstractPayClient<WXPayClientConfig> {
@Override
protected void doInit() {
WxPayConfig payConfig = new WxPayConfig();
BeanUtil.copyProperties(config, payConfig, "keyContent");
BeanUtil.copyProperties(config, payConfig, "privateKeyContent","privateCertContent");
payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式
// if (StrUtil.isNotEmpty(config.getKeyContent())) {
// payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8));
@ -167,7 +168,7 @@ public class WXLitePayClient extends AbstractPayClient<WXPayClientConfig> {
.builder()
.orderExtensionNo(result.getOutTradeNo())
.channelOrderNo(result.getTradeState())
.successTime(DateUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
.successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
.data(data.getBody())
.build();
}
@ -181,7 +182,7 @@ public class WXLitePayClient extends AbstractPayClient<WXPayClientConfig> {
.orderExtensionNo(notifyResult.getOutTradeNo())
.channelOrderNo(notifyResult.getTransactionId())
.channelUserId(notifyResult.getOpenid())
.successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
.successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
.data(data.getBody())
.build();

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.io.FileUtils;
@ -150,7 +151,7 @@ public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> {
.builder()
.orderExtensionNo(result.getOutTradeNo())
.channelOrderNo(result.getTradeState())
.successTime(DateUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
.successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
.data(data.getBody())
.build();
}
@ -164,7 +165,7 @@ public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> {
.orderExtensionNo(notifyResult.getOutTradeNo())
.channelOrderNo(notifyResult.getTransactionId())
.channelUserId(notifyResult.getOpenid())
.successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
.successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
.data(data.getBody())
.build();

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
@ -161,7 +162,7 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
.builder()
.orderExtensionNo(result.getOutTradeNo())
.channelOrderNo(result.getTradeState())
.successTime(DateUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
.successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
.data(data.getBody())
.build();
}
@ -175,7 +176,7 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
.orderExtensionNo(notifyResult.getOutTradeNo())
.channelOrderNo(notifyResult.getTransactionId())
.channelUserId(notifyResult.getOpenid())
.successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
.successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
.data(data.getBody())
.build();

View File

@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
public class YudaoSmsAutoConfiguration {
@Bean

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.sms.core.client.dto;
import lombok.Data;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 消息接收 Response DTO
@ -32,7 +32,7 @@ public class SmsReceiveRespDTO {
/**
* 用户接收时间
*/
private Date receiveTime;
private LocalDateTime receiveTime;
/**
* 短信 API 发送返回的序号

View File

@ -28,7 +28,7 @@ import com.google.common.annotations.VisibleForTesting;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
@ -166,13 +166,13 @@ public class AliyunSmsClient extends AbstractSmsClient {
*/
@JsonProperty("send_time")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private Date sendTime;
private LocalDateTime sendTime;
/**
* 状态报告时间
*/
@JsonProperty("report_time")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private Date reportTime;
private LocalDateTime reportTime;
/**
* 是否接收成功
*/

View File

@ -22,7 +22,7 @@ import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.*;
import lombok.Data;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
@ -240,7 +240,7 @@ public class TencentSmsClient extends AbstractSmsClient {
*/
@JsonProperty("user_receive_time")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private Date receiveTime;
private LocalDateTime receiveTime;
/**
* 国家或地区

View File

@ -2,9 +2,11 @@ package cn.iocoder.yudao.framework.sms.core.client.impl.yunpian;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
@ -12,7 +14,6 @@ import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
@ -23,6 +24,7 @@ import com.yunpian.sdk.model.Template;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
@ -104,6 +106,9 @@ public class YunpianSmsClient extends AbstractSmsClient {
@Override
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable {
return invoke(() -> {
if (!NumberUtil.isNumber(apiTemplateId)) {
throw new IllegalArgumentException("云片的 API 模板编号必须为整数");
}
Map<String, String> request = new HashMap<>();
request.put(YunpianConstant.APIKEY, properties.getApiKey());
request.put(YunpianConstant.TPL_ID, apiTemplateId);
@ -198,7 +203,7 @@ public class YunpianSmsClient extends AbstractSmsClient {
*/
@JsonProperty("user_receive_time")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private Date userReceiveTime;
private LocalDateTime userReceiveTime;
}

View File

@ -11,7 +11,6 @@ import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import com.aliyuncs.AcsRequest;
import com.aliyuncs.IAcsClient;
@ -27,6 +26,7 @@ import org.mockito.ArgumentMatcher;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.time.LocalDateTime;
import java.util.List;
import java.util.function.Function;
@ -125,7 +125,7 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
assertEquals("DELIVERED", statuses.get(0).getErrorCode());
assertEquals("用户接收成功", statuses.get(0).getErrorMsg());
assertEquals("13900000001", statuses.get(0).getMobile());
assertEquals(DateUtils.buildTime(2017, 2, 2, 22, 23, 24), statuses.get(0).getReceiveTime());
assertEquals(LocalDateTime.of(2017, 2, 2, 22, 23, 24), statuses.get(0).getReceiveTime());
assertEquals("12345", statuses.get(0).getSerialNo());
assertEquals(67890L, statuses.get(0).getLogId());
}
@ -181,7 +181,7 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
when(client.getAcsResponse(any(AcsRequest.class))).thenThrow(ex);
// 调用并断言异常
SmsCommonResult<?> result = smsClient.invoke(request,null);
SmsCommonResult<?> result = smsClient.invoke(request, null);
// 断言
assertEquals(ex.getErrCode(), result.getApiCode());
assertEquals(ex.getErrMsg(), result.getApiMsg());

View File

@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
@ -25,6 +24,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@ -146,7 +146,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
assertEquals("DELIVRD", statuses.get(0).getErrorCode());
assertEquals("用户短信送达成功", statuses.get(0).getErrorMsg());
assertEquals("13900000001", statuses.get(0).getMobile());
assertEquals(DateUtils.buildTime(2015, 10, 17, 8, 3, 4), statuses.get(0).getReceiveTime());
assertEquals(LocalDateTime.of(2015, 10, 17, 8, 3, 4), statuses.get(0).getReceiveTime());
assertEquals("12345", statuses.get(0).getSerialNo());
assertEquals(67890L, statuses.get(0).getLogId());
}

View File

@ -10,7 +10,6 @@ import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import com.google.common.collect.Lists;
import com.yunpian.sdk.YunpianClient;
import com.yunpian.sdk.api.SmsApi;
@ -23,6 +22,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -115,7 +115,7 @@ public class YunpianSmsClientTest extends BaseMockitoUnitTest {
assertEquals("", statuses.get(0).getErrorCode());
assertNull(statuses.get(0).getErrorMsg());
assertEquals("15205201314", statuses.get(0).getMobile());
assertEquals(DateUtils.buildTime(2014, 3, 17, 22, 55, 21), statuses.get(0).getReceiveTime());
assertEquals(LocalDateTime.of(2014, 3, 17, 22, 55, 21), statuses.get(0).getReceiveTime());
assertEquals("9527", statuses.get(0).getSerialNo());
assertEquals(1024L, statuses.get(0).getLogId());
}
@ -124,7 +124,7 @@ public class YunpianSmsClientTest extends BaseMockitoUnitTest {
@SuppressWarnings("unchecked")
public void testDoGetSmsTemplate() throws Throwable {
// 准备参数
String apiTemplateId = randomString();
String apiTemplateId = String.valueOf(randomLongId());
// mock tpl 方法
TplApi tplApi = mock(TplApi.class);
when(client.tpl()).thenReturn(tplApi);

View File

@ -19,7 +19,7 @@ import org.springframework.context.annotation.Primary;
* @date 2021-10-30
*/
@Slf4j
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(JustAuthProperties.class)
public class YudaoSocialAutoConfiguration {

View File

@ -35,7 +35,7 @@ import org.springframework.data.redis.core.RedisTemplate;
import java.util.Objects;
@Configuration
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户
@EnableConfigurationProperties(TenantProperties.class)
public class YudaoTenantAutoConfiguration {
@ -119,6 +119,8 @@ public class YudaoTenantAutoConfiguration {
};
}
// ========== Redis ==========
@Bean
@Primary // 引入租户时tenantRedisCacheManager 为主 Bean
public RedisCacheManager tenantRedisCacheManager(RedisTemplate<String, Object> redisTemplate,

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.tenant.core.context;
import cn.iocoder.yudao.framework.common.enums.DocumentEnum;
import com.alibaba.ttl.TransmittableThreadLocal;
/**
@ -36,7 +37,8 @@ public class TenantContextHolder {
public static Long getRequiredTenantId() {
Long tenantId = getTenantId();
if (tenantId == null) {
throw new NullPointerException("TenantContextHolder 不存在租户编号"); // TODO 芋艿增加文档链接
throw new NullPointerException("TenantContextHolder 不存在租户编号!可参考文档:"
+ DocumentEnum.TENANT.getUrl());
}
return tenantId;
}

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.tenant.core.redis;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.cache.Cache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
@ -11,7 +10,7 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
/**
* 多租户的 {@link RedisCacheManager} 实现类
*
* 操作指定 name {@link Cache} 自动拼接租户后缀格式为 name + ":" + tenantId
* 操作指定 name {@link Cache} 自动拼接租户后缀格式为 name + ":" + tenantId + 后缀
*
* @author airhead
*/
@ -24,7 +23,7 @@ public class TenantRedisCacheManager extends RedisCacheManager {
}
@Override
public Cache getCache(@NotNull String name) {
public Cache getCache(String name) {
// 如果开启多租户 name 拼接租户后缀
if (!TenantContextHolder.isIgnore()
&& TenantContextHolder.getTenantId() != null) {

View File

@ -28,8 +28,6 @@ import java.util.Objects;
* 2. 如果请求未带租户的编号检查是否是忽略的 URL否则也不允许访问
* 3. 校验租户是合法例如说被禁用到期
*
* 校验用户访问的租户是否是其所在的租户
*
* @author 芋道源码
*/
@Slf4j

View File

@ -8,7 +8,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
@Configuration
@Configuration(proxyBeanMethods = false)
public class YudaoCaptchaConfiguration {
static {

View File

@ -1,37 +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-config</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>配置中心,基于 Apollo 魔改实现</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</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId> <!-- 引入 Apollo Client 库,实现内嵌的配置中心 -->
</dependency>
</dependencies>
</project>

View File

@ -1,22 +0,0 @@
package cn.iocoder.yudao.framework.apollo.core;
import cn.iocoder.yudao.framework.apollo.internals.ConfigFrameworkDAO;
/**
* 针对 {@link com.ctrip.framework.apollo.core.ConfigConsts} 的补充主要增加
*
* 1. apollo.jdbc.* 配置项的枚举
*
* @author 芋道源码
*/
public class ConfigConsts {
/**
* {@link ConfigFrameworkDAO} 的实现类
*/
public static final String APOLLO_JDBC_DAO = "apollo.jdbc.dao";
public static final String APOLLO_JDBC_URL = "apollo.jdbc.url";
public static final String APOLLO_JDBC_USERNAME = "apollo.jdbc.username";
public static final String APOLLO_JDBC_PASSWORD = "apollo.jdbc.password";
}

View File

@ -1,32 +0,0 @@
package cn.iocoder.yudao.framework.apollo.internals;
import cn.iocoder.yudao.framework.apollo.internals.dto.ConfigRespDTO;
import java.util.Date;
import java.util.List;
/**
* 配置 Framework DAO 接口
*
* 注意实现类必须提供 (String jdbcUrl, String username, String password) 构造方法
*
* @author 芋道源码
*/
public interface ConfigFrameworkDAO {
/**
* 查询是否存在比 maxUpdateTime 的更新记录数量
*
* @param maxUpdateTime 最大更新时间
* @return 是否存在
*/
int selectCountByUpdateTimeGt(Date maxUpdateTime);
/**
* 查询配置列表
*
* @return 配置列表
*/
List<ConfigRespDTO> selectList();
}

View File

@ -1,183 +0,0 @@
package cn.iocoder.yudao.framework.apollo.internals;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.apollo.core.ConfigConsts;
import cn.iocoder.yudao.framework.apollo.internals.dto.ConfigRespDTO;
import com.ctrip.framework.apollo.Apollo;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.internals.AbstractConfigRepository;
import com.ctrip.framework.apollo.internals.ConfigRepository;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Constructor;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@Slf4j
public class DBConfigRepository extends AbstractConfigRepository {
private final static ScheduledExecutorService m_executorService;
private static DBConfigRepository INSTANCE;
static {
m_executorService = Executors.newScheduledThreadPool(1,
ApolloThreadFactory.create(DBConfigRepository.class.getSimpleName(), true));
}
private final ConfigUtil m_configUtil;
private final PropertiesFactory propertiesFactory;
private final String m_namespace;
/**
* 配置缓存使用 Properties 存储
*/
private volatile Properties m_configCache;
/**
* 缓存配置的最大更新时间用于后续的增量轮询判断是否有更新
*/
private volatile Date maxUpdateTime;
/**
* 配置读取 DAO
*/
private final ConfigFrameworkDAO configFrameworkDAO;
public DBConfigRepository(String namespace) {
// 初始化变量
this.m_namespace = namespace;
this.propertiesFactory = ApolloInjector.getInstance(PropertiesFactory.class);
this.m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
// 初始化 DB
this.configFrameworkDAO = createConfigFrameworkDAO();
// 初始化加载
this.trySync();
// 初始化定时任务
this.schedulePeriodicRefresh();
// 设置单例
INSTANCE = this;
}
@SneakyThrows
private static ConfigFrameworkDAO createConfigFrameworkDAO() {
String dao = System.getProperty(ConfigConsts.APOLLO_JDBC_DAO);
String url = System.getProperty(ConfigConsts.APOLLO_JDBC_URL);
String username = System.getProperty(ConfigConsts.APOLLO_JDBC_USERNAME);
String password = System.getProperty(ConfigConsts.APOLLO_JDBC_PASSWORD);
// 创建 DBConfigRepository 对象
Class<? extends ConfigFrameworkDAO> clazz = ClassUtil.loadClass(dao);
Constructor<? extends ConfigFrameworkDAO> constructor = ReflectUtil.getConstructor(clazz, String.class, String.class, String.class);
return constructor.newInstance(url, username, password);
}
/**
* 通知同步
*/
public static void noticeSync() {
// 提交到线程池中避免和 schedulePeriodicRefresh 并发问题
m_executorService.submit(() -> {
INSTANCE.trySync();
});
}
@Override
protected void sync() {
// 第一步尝试获取配置
List<ConfigRespDTO> configs = this.loadConfigIfUpdate(this.maxUpdateTime);
if (CollUtil.isEmpty(configs)) { // 如果没有更新则返回
return;
}
// 第二步构建新的 Properties
Properties newProperties = this.buildProperties(configs);
this.m_configCache = newProperties;
// 第三步获取最大的配置时间
assert configs.size() > 0; // 断言避免告警
this.maxUpdateTime = configs.stream().max(Comparator.comparing(ConfigRespDTO::getUpdateTime)).get().getUpdateTime();
// 第四部触发配置刷新重要
super.fireRepositoryChange(m_namespace, newProperties);
log.info("[sync][缓存配置,数量为:{}]", configs.size());
}
@Override
public Properties getConfig() {
// 兜底避免可能存在配置为 null 的情况
if (m_configCache == null) {
this.trySync();
}
// 返回配置
return m_configCache;
}
@Override
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {
// 啥事不做
}
@Override
public ConfigSourceType getSourceType() {
return ConfigSourceType.REMOTE;
}
private Properties buildProperties(List<ConfigRespDTO> configs) {
Properties properties = propertiesFactory.getPropertiesInstance();
configs.stream().filter(config -> !config.getDeleted()) // 过滤掉被删除的配置
.forEach(config -> properties.put(config.getKey(), config.getValue()));
return properties;
}
// ========== 定时器相关操作 ==========
private void schedulePeriodicRefresh() {
log.debug("Schedule periodic refresh with interval: {} {}",
m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());
m_executorService.scheduleAtFixedRate(() -> {
Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));
log.debug("refresh config for namespace: {}", m_namespace);
// 执行同步. 内部已经 try catch 掉异常无需在处理
trySync();
Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
}, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
m_configUtil.getRefreshIntervalTimeUnit());
}
// ========== 数据库相关操作 ==========
/**
* 如果配置发生变化从数据库中获取最新的全量配置
* 如果未发生变化则返回空
*
* @param maxUpdateTime 当前配置的最大更新时间
* @return 配置列表
*/
private List<ConfigRespDTO> loadConfigIfUpdate(Date maxUpdateTime) {
// 第一步判断是否要更新
if (maxUpdateTime == null) { // 如果更新时间为空说明 DB 一定有新数据
log.info("[loadConfigIfUpdate][首次加载全量配置]");
} else { // 判断数据库中是否有更新的配置
if (configFrameworkDAO.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
return null;
}
log.info("[loadConfigIfUpdate][增量加载全量配置]");
}
// 第二步如果有更新则从数据库加载所有配置
return configFrameworkDAO.selectList();
}
}

View File

@ -1,75 +0,0 @@
package cn.iocoder.yudao.framework.apollo.internals;
import cn.iocoder.yudao.framework.apollo.spi.DBConfigFactory;
import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
import com.ctrip.framework.apollo.internals.*;
import com.ctrip.framework.apollo.spi.*;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.ctrip.framework.apollo.util.http.DefaultHttpClient;
import com.ctrip.framework.apollo.util.yaml.YamlParser;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Singleton;
/**
* Guice injector
*
* 基于 Guice 注入器实现类
*
* @author Jason Song(song_s@ctrip.com)
*/
public class DefaultXInjector implements Injector {
private final com.google.inject.Injector m_injector;
public DefaultXInjector() {
try {
m_injector = Guice.createInjector(new ApolloModule());
} catch (Throwable ex) {
ApolloConfigException exception = new ApolloConfigException("Unable to initialize Guice Injector!", ex);
Tracer.logError(exception);
throw exception;
}
}
@Override
public <T> T getInstance(Class<T> clazz) {
try {
return m_injector.getInstance(clazz);
} catch (Throwable ex) {
Tracer.logError(ex);
throw new ApolloConfigException(String.format("Unable to load instance for %s!", clazz.getName()), ex);
}
}
@Override
public <T> T getInstance(Class<T> clazz, String name) {
// Guice does not support get instance by type and name
return null;
}
private static class ApolloModule extends AbstractModule {
@Override
protected void configure() {
bind(ConfigManager.class).to(DefaultConfigManager.class).in(Singleton.class);
bind(ConfigFactoryManager.class).to(DefaultConfigFactoryManager.class).in(Singleton.class);
bind(ConfigRegistry.class).to(DefaultConfigRegistry.class).in(Singleton.class);
// 自定义 ConfigFactory 实现使用 DB 作为数据源
bind(ConfigFactory.class).to(DBConfigFactory.class).in(Singleton.class);
bind(ConfigUtil.class).in(Singleton.class);
bind(DefaultHttpClient.class).in(Singleton.class);
bind(ConfigServiceLocator.class).in(Singleton.class);
bind(RemoteConfigLongPollService.class).in(Singleton.class);
bind(YamlParser.class).in(Singleton.class);
bind(PropertiesFactory.class).to(DefaultPropertiesFactory.class).in(Singleton.class);
}
}
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.framework.apollo.internals.dto;
import lombok.Data;
import java.util.Date;
/**
* 配置 Response DTO
*
* @author 芋道源码
*/
@Data
public class ConfigRespDTO {
/**
* 参数键名
*/
private String key;
/**
* 参数键值
*/
private String value;
/**
* 是否删除
*/
private Boolean deleted;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@ -1,13 +0,0 @@
/**
* 配置中心客户端基于 Apollo Client 进行简化
*
* 差别在于我们使用 cn.iocoder.yudao.modules.infra.dal.dataobject.config.InfConfigDO 表作为配置源
* 当然功能肯定也会相对少些满足最小化诉求
*
* 1. 项目初始化时可以使用 SysConfigDO 表的配置
* 2. 使用 Spring @Value 可以注入属性
* 3. SysConfigDO 表的配置修改时注入到 @Value 的属性可以刷新
*
* 另外整个包结构会参考 Apollo 为主方便维护与理解
*/
package cn.iocoder.yudao.framework.apollo;

View File

@ -1,32 +0,0 @@
package cn.iocoder.yudao.framework.apollo.spi;
import cn.iocoder.yudao.framework.apollo.internals.DBConfigRepository;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigFile;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.internals.ConfigRepository;
import com.ctrip.framework.apollo.internals.DefaultConfig;
import com.ctrip.framework.apollo.spi.ConfigFactory;
/**
* 基于 DB ConfigFactory 实现类
*
* @author 芋道源码
*/
public class DBConfigFactory implements ConfigFactory {
@Override
public Config create(String namespace) {
return new DefaultConfig(namespace, this.createDBConfigRepository(namespace));
}
@Override
public ConfigFile createConfigFile(String namespace, ConfigFileFormat configFileFormat) {
throw new UnsupportedOperationException("暂不支持 Apollo 配置文件");
}
private ConfigRepository createDBConfigRepository(String namespace) {
return new DBConfigRepository(namespace);
}
}

View File

@ -1,63 +0,0 @@
package cn.iocoder.yudao.framework.apollo.spring.boot;
import cn.iocoder.yudao.framework.apollo.core.ConfigConsts;
import com.google.common.base.Strings;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* {@link com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer} 的补充目前的目的有
*
* 1. 将自定义的 apollo.jdbc 设置到 System 变量中
*
* @author 芋道源码
*/
public class ApolloApplicationContextInitializer implements EnvironmentPostProcessor, Ordered {
/**
* 优先级更高要早于 Apollo ApolloApplicationContextInitializer 的初始化
*/
public static final int DEFAULT_ORDER = -1;
private int order = DEFAULT_ORDER;
private static final String[] APOLLO_SYSTEM_PROPERTIES = {ConfigConsts.APOLLO_JDBC_DAO,
ConfigConsts.APOLLO_JDBC_URL, ConfigConsts.APOLLO_JDBC_USERNAME, ConfigConsts.APOLLO_JDBC_PASSWORD};
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
initializeSystemProperty(environment);
}
/**
* To fill system properties from environment config
*/
void initializeSystemProperty(ConfigurableEnvironment environment) {
for (String propertyName : APOLLO_SYSTEM_PROPERTIES) {
fillSystemPropertyFromEnvironment(environment, propertyName);
}
}
private void fillSystemPropertyFromEnvironment(ConfigurableEnvironment environment, String propertyName) {
if (System.getProperty(propertyName) != null) {
return;
}
String propertyValue = environment.getProperty(propertyName);
if (Strings.isNullOrEmpty(propertyValue)) {
return;
}
System.setProperty(propertyName, propertyValue);
}
@Override
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}

View File

@ -1 +0,0 @@
cn.iocoder.yudao.framework.apollo.internals.DefaultXInjector

View File

@ -1,2 +0,0 @@
org.springframework.boot.env.EnvironmentPostProcessor=\
cn.iocoder.yudao.framework.apollo.spring.boot.ApolloApplicationContextInitializer

View File

@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
public class YudaoFileAutoConfiguration {
@Bean

View File

@ -75,7 +75,8 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
// 阿里云必须有 region否则会报错
if (config.getEndpoint().contains(ENDPOINT_ALIYUN)) {
return StrUtil.subBefore(config.getEndpoint(), '.', false)
.replaceAll("-internal", ""); // 去除内网 Endpoint 的后缀
.replaceAll("-internal", "")// 去除内网 Endpoint 的后缀
.replaceAll("https://", "");
}
return null;
}

View File

@ -8,7 +8,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@Configuration(proxyBeanMethods = false)
public class YudaoFlowableConfiguration {
/**

View File

@ -11,7 +11,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* 异步任务 Configuration
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableAsync
public class YudaoAsyncAutoConfiguration {

View File

@ -9,7 +9,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 定时任务 Configuration
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableScheduling // 开启 Spring 自带的定时任务
public class YudaoQuartzAutoConfiguration {

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.quartz.core.handler;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.ThreadUtil;
import cn.iocoder.yudao.framework.quartz.core.enums.JobDataKeyEnum;
@ -13,10 +14,10 @@ import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
import javax.annotation.Resource;
import java.util.Date;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.diff;
/**
* 基础 Job 调用者负责调用 {@link JobHandler#execute(String)} 执行任务
@ -46,7 +47,7 @@ public class JobHandlerInvoker extends QuartzJobBean {
// 第二步执行任务
Long jobLogId = null;
Date startTime = new Date();
LocalDateTime startTime = LocalDateTime.now();
String data = null;
Throwable exception = null;
try {
@ -73,9 +74,9 @@ public class JobHandlerInvoker extends QuartzJobBean {
return jobHandler.execute(jobHandlerParam);
}
private void updateJobLogResultAsync(Long jobLogId, Date startTime, String data, Throwable exception,
private void updateJobLogResultAsync(Long jobLogId, LocalDateTime startTime, String data, Throwable exception,
JobExecutionContext executionContext) {
Date endTime = new Date();
LocalDateTime endTime = LocalDateTime.now();
// 处理是否成功
boolean success = exception == null;
if (!success) {
@ -83,7 +84,7 @@ public class JobHandlerInvoker extends QuartzJobBean {
}
// 更新日志
try {
jobLogFrameworkService.updateJobLogResultAsync(jobLogId, endTime, (int) diff(endTime, startTime), success, data);
jobLogFrameworkService.updateJobLogResultAsync(jobLogId, endTime, (int) LocalDateTimeUtil.between(startTime, endTime).toMillis(), success, data);
} catch (Exception ex) {
log.error("[executeInternal][Job({}) logId({}) 记录执行日志失败({}/{})]",
executionContext.getJobDetail().getKey(), jobLogId, success, data);

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.quartz.core.service;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.time.LocalDateTime;
/**
* Job 日志 Framework Service 接口
@ -22,7 +22,7 @@ public interface JobLogFrameworkService {
* @return Job 日志的编号
*/
Long createJobLog(@NotNull(message = "任务编号不能为空") Long jobId,
@NotNull(message = "开始时间") Date beginTime,
@NotNull(message = "开始时间") LocalDateTime beginTime,
@NotEmpty(message = "Job 处理器的名字不能为空") String jobHandlerName,
String jobHandlerParam,
@NotNull(message = "第几次执行不能为空") Integer executeIndex);
@ -37,7 +37,7 @@ public interface JobLogFrameworkService {
* @param result 成功数据
*/
void updateJobLogResultAsync(@NotNull(message = "日志编号不能为空") Long logId,
@NotNull(message = "结束时间不能为空") Date endTime,
@NotNull(message = "结束时间不能为空") LocalDateTime endTime,
@NotNull(message = "运行时长不能为空") Integer duration,
boolean success, String result);

View File

@ -1,8 +1,10 @@
package cn.iocoder.yudao.framework.quartz.core.util;
import cn.hutool.core.date.LocalDateTimeUtil;
import org.quartz.CronExpression;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@ -31,7 +33,7 @@ public class CronUtils {
* @param n 数量
* @return 满足条件的执行时间
*/
public static List<Date> getNextTimes(String cronExpression, int n) {
public static List<LocalDateTime> getNextTimes(String cronExpression, int n) {
// 获得 CronExpression 对象
CronExpression cron;
try {
@ -41,10 +43,10 @@ public class CronUtils {
}
// 从当前开始计算n 个满足条件的
Date now = new Date();
List<Date> nextTimes = new ArrayList<>(n);
List<LocalDateTime> nextTimes = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
Date nextTime = cron.getNextValidTimeAfter(now);
nextTimes.add(nextTime);
nextTimes.add(LocalDateTimeUtil.of(nextTime));
// 切换现在为下一个触发时间
now = nextTime;
}

View File

@ -13,7 +13,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({MeterRegistryCustomizer.class})
@ConditionalOnProperty(prefix = "yudao.metrics", value = "enable", matchIfMissing = true) // 允许使用 yudao.metrics.enable=false 禁用 Metrics
public class YudaoMetricsAutoConfiguration {

View File

@ -15,7 +15,7 @@ import org.springframework.context.annotation.Configuration;
*
* @author mashu
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({BizTraceAspect.class})
@EnableConfigurationProperties(TracerProperties.class)
@ConditionalOnProperty(prefix = "yudao.tracer", value = "enable", matchIfMissing = true)

View File

@ -25,6 +25,7 @@ import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.stream.DefaultStreamMessageListenerContainerX;
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
import org.springframework.scheduling.annotation.Async;
import java.util.List;
import java.util.Properties;
@ -34,7 +35,7 @@ import java.util.Properties;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(YudaoRedisAutoConfiguration.class)
@Slf4j
public class YudaoMQAutoConfiguration {
@ -54,6 +55,7 @@ public class YudaoMQAutoConfiguration {
* 创建 Redis Pub/Sub 广播消费的容器
*/
@Bean
@Async // 异步化可降低 2 秒左右的启动时间
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisMQTemplate redisMQTemplate, List<AbstractChannelMessageListener<?>> listeners) {
// 创建 RedisMessageListenerContainer 对象
@ -95,6 +97,8 @@ public class YudaoMQAutoConfiguration {
// 第二步注册监听器消费对应的 Stream 主题
String consumerName = buildConsumerName();
listeners.parallelStream().forEach(listener -> {
log.info("[redisStreamMessageListenerContainer][开始注册 StreamKey({}) 对应的监听器({})]",
listener.getStreamKey(), listener.getClass().getName());
// 创建 listener 对应的消费者分组
try {
redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup());
@ -111,6 +115,8 @@ public class YudaoMQAutoConfiguration {
.autoAcknowledge(false) // 不自动 ack
.cancelOnError(throwable -> false); // 默认配置发生异常就取消消费显然不符合预期因此我们设置为 false
container.register(builder.build(), listener);
log.info("[redisStreamMessageListenerContainer][完成注册 StreamKey({}) 对应的监听器({})]",
listener.getStreamKey(), listener.getClass().getName());
});
return container;
}

View File

@ -59,13 +59,6 @@
<artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId> <!-- 加解密 -->
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -14,7 +14,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理
@EnableConfigurationProperties(DruidStatProperties.class)
public class YudaoDataSourceAutoConfiguration {

View File

@ -23,7 +23,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@MapperScan(value = "${yudao.info.base-package}", annotationClass = Mapper.class,
lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载目前仅用于单元测试
public class YudaoMybatisAutoConfiguration {

View File

@ -7,7 +7,7 @@ import lombok.Data;
import org.apache.ibatis.type.JdbcType;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 基础实体对象
@ -21,12 +21,12 @@ public abstract class BaseDO implements Serializable {
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
private LocalDateTime createTime;
/**
* 最后更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
private LocalDateTime updateTime;
/**
* 创建者目前使用 SysUser id 编号
*

View File

@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.Objects;
/**
@ -22,7 +22,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler {
if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) {
BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();
Date current = new Date();
LocalDateTime current = LocalDateTime.now();
// 创建时间为空则以当前时间为插入时间
if (Objects.isNull(baseDO.getCreateTime())) {
baseDO.setCreateTime(current);
@ -49,7 +49,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler {
// 更新时间为空则以当前时间为更新时间
Object modifyTime = getFieldValByName("updateTime", metaObject);
if (Objects.isNull(modifyTime)) {
setFieldValByName("updateTime", new Date(), metaObject);
setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
// 当前登录用户不为空更新人为空则当前登录用户为更新人

View File

@ -1,10 +1,11 @@
package cn.iocoder.yudao.framework.mybatis.core.type;
import cn.hutool.core.lang.Assert;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.extra.spring.SpringUtil;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.jasypt.encryption.StringEncryptor;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
@ -12,18 +13,20 @@ import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 字段字段的 TypeHandler 实现类基于 {@link StringEncryptor} 实现
* 字段字段的 TypeHandler 实现类基于 {@link cn.hutool.crypto.symmetric.AES} 实现
* 可通过 jasypt.encryptor.password 配置项设置密钥
*
* @author 芋道源码
*/
public class EncryptTypeHandler extends BaseTypeHandler<String> {
private static StringEncryptor encryptor;
private static final String ENCRYPTOR_PROPERTY_NAME = "mybatis-plus.encryptor.password";
private static AES aes;
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, getEncryptor().encrypt(parameter));
ps.setString(i, encrypt(parameter));
}
@Override
@ -48,23 +51,25 @@ public class EncryptTypeHandler extends BaseTypeHandler<String> {
if (value == null) {
return null;
}
return getEncryptor().decrypt(value);
return getEncryptor().decryptStr(value);
}
public static String encrypt(String rawValue) {
if (rawValue == null) {
return null;
}
return getEncryptor().encrypt(rawValue);
return getEncryptor().encryptBase64(rawValue);
}
private static StringEncryptor getEncryptor() {
if (encryptor != null) {
return encryptor;
private static AES getEncryptor() {
if (aes != null) {
return aes;
}
encryptor = SpringUtil.getBean(StringEncryptor.class);
Assert.notNull(encryptor, "StringEncryptor 不能为空");
return encryptor;
// 构建 AES
String password = SpringUtil.getProperty(ENCRYPTOR_PROPERTY_NAME);
Assert.notEmpty(password, "配置项({}) 不能为空", ENCRYPTOR_PROPERTY_NAME);
aes = SecureUtil.aes(password.getBytes());
return aes;
}
}

View File

@ -0,0 +1,57 @@
package cn.iocoder.yudao.framework.mybatis.core.type;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
/**
* List<Long> 的类型转换器实现类对应数据库的 varchar 类型
*
* @author 芋道源码
*/
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
public class LongListTypeHandler implements TypeHandler<List<Long>> {
private static final String COMMA = ",";
@Override
public void setParameter(PreparedStatement ps, int i, List<Long> strings, JdbcType jdbcType) throws SQLException {
// 设置占位符
ps.setString(i, CollUtil.join(strings, COMMA));
}
@Override
public List<Long> getResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName);
return getResult(value);
}
@Override
public List<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex);
return getResult(value);
}
@Override
public List<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex);
return getResult(value);
}
private List<Long> getResult(String value) {
if (value == null) {
return null;
}
return StrUtils.splitToLong(value, COMMA);
}
}

View File

@ -8,7 +8,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(LockAutoConfiguration.class)
public class YudaoLock4jConfiguration {

View File

@ -13,7 +13,7 @@ import org.springframework.data.redis.serializer.RedisSerializer;
/**
* Cache 配置类基于 Redis 实现
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({CacheProperties.class})
@EnableCaching
public class YudaoCacheAutoConfiguration {

View File

@ -9,7 +9,7 @@ import org.springframework.data.redis.serializer.RedisSerializer;
/**
* Redis 配置类
*/
@Configuration
@Configuration(proxyBeanMethods = false)
public class YudaoRedisAutoConfiguration {
/**

View File

@ -32,7 +32,7 @@ import java.util.Set;
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.test.core.util;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
@ -8,6 +9,7 @@ import uk.co.jemos.podam.api.PodamFactory;
import uk.co.jemos.podam.api.PodamFactoryImpl;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@ -76,6 +78,10 @@ public class RandomUtils {
return RandomUtil.randomDay(0, RANDOM_DATE_MAX);
}
public static LocalDateTime randomLocalDateTime() {
return LocalDateTimeUtil.of(randomDate());
}
public static Short randomShort() {
return (short) RandomUtil.randomInt(0, Short.MAX_VALUE);
}

View File

@ -19,7 +19,7 @@ import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
@Configuration
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(YudaoWebAutoConfiguration.class)
public class YudaoApiLogAutoConfiguration {

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.apilog.core.filter;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.extra.servlet.ServletUtil;
@ -7,7 +8,6 @@ import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLog;
import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.web.config.WebProperties;
@ -20,7 +20,8 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
@ -47,7 +48,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 获得开始时间
Date beginTim = new Date();
LocalDateTime beginTime = LocalDateTime.now();
// 提前获得参数避免 XssFilter 过滤处理
Map<String, String> queryString = ServletUtil.getParamMap(request);
String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtil.getBody(request) : null;
@ -56,15 +57,15 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
// 继续过滤器
filterChain.doFilter(request, response);
// 正常执行记录日志
createApiAccessLog(request, beginTim, queryString, requestBody, null);
createApiAccessLog(request, beginTime, queryString, requestBody, null);
} catch (Exception ex) {
// 异常执行记录日志
createApiAccessLog(request, beginTim, queryString, requestBody, ex);
createApiAccessLog(request, beginTime, queryString, requestBody, ex);
throw ex;
}
}
private void createApiAccessLog(HttpServletRequest request, Date beginTime,
private void createApiAccessLog(HttpServletRequest request, LocalDateTime beginTime,
Map<String, String> queryString, String requestBody, Exception ex) {
ApiAccessLog accessLog = new ApiAccessLog();
try {
@ -75,7 +76,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
}
}
private void buildApiAccessLogDTO(ApiAccessLog accessLog, HttpServletRequest request, Date beginTime,
private void buildApiAccessLogDTO(ApiAccessLog accessLog, HttpServletRequest request, LocalDateTime beginTime,
Map<String, String> queryString, String requestBody, Exception ex) {
// 处理用户信息
accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request));
@ -103,8 +104,8 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
accessLog.setUserIp(ServletUtil.getClientIP(request));
// 持续时间
accessLog.setBeginTime(beginTime);
accessLog.setEndTime(new Date());
accessLog.setDuration((int) DateUtils.diff(accessLog.getEndTime(), accessLog.getBeginTime()));
accessLog.setEndTime(LocalDateTime.now());
accessLog.setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.SECONDS));
}
}

View File

@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.apilog.core.service;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.time.LocalDateTime;
/**
* API 访问日志
@ -61,12 +61,12 @@ public class ApiAccessLog {
* 开始请求时间
*/
@NotNull(message = "开始请求时间不能为空")
private Date beginTime;
private LocalDateTime beginTime;
/**
* 结束请求时间
*/
@NotNull(message = "结束请求时间不能为空")
private Date endTime;
private LocalDateTime endTime;
/**
* 执行时长单位毫秒
*/

View File

@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.apilog.core.service;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.time.LocalDateTime;
/**
* API 错误日志
@ -61,7 +61,7 @@ public class ApiErrorLog {
* 异常时间
*/
@NotNull(message = "异常时间不能为空")
private Date exceptionTime;
private LocalDateTime exceptionTime;
/**
* 异常名
*/

View File

@ -13,8 +13,8 @@ import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
@Configuration(proxyBeanMethods = false)
@Slf4j
@Configuration
public class YudaoJacksonAutoConfiguration {
@Bean

View File

@ -29,7 +29,7 @@ import static springfox.documentation.builders.RequestHandlerSelectors.basePacka
*
* @author 芋道源码
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableSwagger2
@EnableKnife4j
@ConditionalOnClass({Docket.class, ApiInfoBuilder.class})

View File

@ -26,7 +26,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
import javax.servlet.Filter;
@Configuration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({WebProperties.class, XssProperties.class})
public class YudaoWebAutoConfiguration implements WebMvcConfigurer {

View File

@ -31,7 +31,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;
@ -78,7 +78,7 @@ public class GlobalExceptionHandler {
return validationException((ValidationException) ex);
}
if (ex instanceof NoHandlerFoundException) {
return noHandlerFoundExceptionHandler((NoHandlerFoundException) ex);
return noHandlerFoundExceptionHandler(request, (NoHandlerFoundException) ex);
}
if (ex instanceof HttpRequestMethodNotSupportedException) {
return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex);
@ -167,7 +167,7 @@ public class GlobalExceptionHandler {
* 2. spring.mvc.static-path-pattern /statics/**
*/
@ExceptionHandler(NoHandlerFoundException.class)
public CommonResult<?> noHandlerFoundExceptionHandler(NoHandlerFoundException ex) {
public CommonResult<?> noHandlerFoundExceptionHandler(HttpServletRequest req, NoHandlerFoundException ex) {
log.warn("[noHandlerFoundExceptionHandler]", ex);
return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getRequestURL()));
}
@ -267,7 +267,7 @@ public class GlobalExceptionHandler {
errorLog.setRequestMethod(request.getMethod());
errorLog.setUserAgent(ServletUtils.getUserAgent(request));
errorLog.setUserIp(ServletUtil.getClientIP(request));
errorLog.setExceptionTime(new Date());
errorLog.setExceptionTime(LocalDateTime.now());
}
}

View File

@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
@ApiModel("管理后台 - 动态表单 Response VO")
@ -28,6 +28,6 @@ public class BpmFormRespVO extends BpmFormBaseVO {
private List<String> fields;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
private LocalDateTime createTime;
}

View File

@ -9,7 +9,7 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import java.time.LocalDateTime;
@ApiModel("管理后台 - 用户组分页 Request VO")
@Data
@ -25,6 +25,6 @@ public class BpmUserGroupPageReqVO extends PageParam {
@DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "创建时间")
private Date[] createTime;
private LocalDateTime[] createTime;
}

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 用户组 Response VO")
@ -14,6 +16,6 @@ public class BpmUserGroupRespVO extends BpmUserGroupBaseVO {
private Long id;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
private LocalDateTime createTime;
}

View File

@ -6,7 +6,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Date;
import java.time.LocalDateTime;
@ApiModel("管理后台 - 流程模型的分页的每一项 Response VO")
@Data
@ -21,7 +21,7 @@ public class BpmModelPageItemRespVO extends BpmModelBaseVO {
private String formName;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
private LocalDateTime createTime;
/**
* 最新部署的流程定义
@ -39,7 +39,7 @@ public class BpmModelPageItemRespVO extends BpmModelBaseVO {
private Integer version;
@ApiModelProperty(value = "部署时间", required = true)
private Date deploymentTime;
private LocalDateTime deploymentTime;
@ApiModelProperty(value = "中断状态", required = true, example = "1", notes = "参见 SuspensionState 枚举")
private Integer suspensionState;

View File

@ -6,7 +6,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Date;
import java.time.LocalDateTime;
@ApiModel("管理后台 - 流程模型的创建 Request VO")
@Data
@ -21,6 +21,6 @@ public class BpmModelRespVO extends BpmModelBaseVO {
private String bpmnXml;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
private LocalDateTime createTime;
}

View File

@ -6,7 +6,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Date;
import java.time.LocalDateTime;
@ApiModel("管理后台 - 流程定义的分页的每一项 Response VO")
@Data
@ -18,6 +18,6 @@ public class BpmProcessDefinitionPageItemRespVO extends BpmProcessDefinitionResp
private String formName;
@ApiModelProperty(value = "部署时间", required = true)
private Date deploymentTime;
private LocalDateTime deploymentTime;
}

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