diff --git a/README.md b/README.md index 312f3e1f..9c5dbd78 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![码云Gitee](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) +[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]() +[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]() 基于 RuoYi-Vue 集成 Mybatis-Plus Lombok Hutool 等便捷开发工具 适配重写相关业务 便于开发 定期与 RuoYi-Vue 同步 * 前端开发框架 Vue、Element UI @@ -16,9 +18,15 @@ * 监控框架 spring-boot-admin 全方位服务监控 * 校验框架 validation 增强接口安全性 严谨性 * 文档框架 knife4j 美化接口文档 +* 序列化框架 统一使用 jackson 高效可靠 * 代码生成器 一键生成前后端代码 +* 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构 +* Redis客户端 采用 Redisson 性能更强 +* 分布式锁 Lock4j 注解锁、工具锁 多种多样 ## 参考文档 + +使用框架前请仔细阅读文档重点注意事项 [参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages) ## 修改RuoYi功能 @@ -34,7 +42,10 @@ * 集成 Feign 接口化管理 Http 请求(如三方请求 支付,短信,推送等) * 移除 自带服务监控 改为 spring-boot-admin 全方位监控 * 增加 demo 模块示例(给不会增加模块的小伙伴做参考) -* 增加 redisson 支持分布式锁 功能更强大 +* 增加 redisson 高性能 Redis 客户端 +* 移除 fastjson 统一使用 jackson 序列化 +* 集成 dynamic-datasource 多数据源(默认支持MySQL,其他种类需自行适配) +* 集成 Lock4j 实现分布式 注解锁、工具锁 多种多样 ### 代码改动 @@ -46,6 +57,7 @@ * 项目修改为 maven多环境配置 * 项目配置修改为 application.yml 统一管理 * 数据权限修改为 适配支持单表、多表 +* 使用 redisson 实现分布式锁注解与工具类 * 使用 redisson 实现 spring-cache 整合 * 增加 mybatis-plus 二级缓存 redis 存储 diff --git a/pom.xml b/pom.xml index 4a73f1ea..5fe335a1 100644 --- a/pom.xml +++ b/pom.xml @@ -14,23 +14,24 @@ 2.3.2 - 2.3.11.RELEASE + 2.4.7 UTF-8 UTF-8 1.8 3.1.1 1.2.6 3.0.2 - 1.2.76 4.1.2 1.7 0.9.1 3.4.3 - 5.6.5 - 2.2.6.RELEASE + 5.7.2 + 3.0.3 11.0 - 2.3.1 + 2.4.1 3.15.2 + 2.2.1 + 3.4.0 @@ -73,13 +74,6 @@ ${velocity.version} - - - com.alibaba - fastjson - ${fastjson.version} - - io.jsonwebtoken @@ -87,6 +81,13 @@ ${jwt.version} + + + com.baomidou + dynamic-datasource-spring-boot-starter + ${datasource.version} + + com.baomidou mybatis-plus-boot-starter @@ -97,6 +98,7 @@ mybatis-plus-extension ${mybatis-plus.version} + cn.hutool hutool-all @@ -132,7 +134,11 @@ redisson-spring-boot-starter ${redisson.version} - + + com.baomidou + lock4j-redisson-spring-boot-starter + ${lock4j.version} + com.ruoyi @@ -270,6 +276,45 @@ warn + + + + jdk8 + + true + 1.8 + + + 1.8 + + + + jdk11 + + 11 + + + 11 + 3.0.1 + + + + + + com.sun.xml.bind + jaxb-impl + ${jaxb.version} + + + + + + + com.sun.xml.bind + jaxb-impl + + + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java index 5b55927b..3a1a4aef 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java @@ -2,15 +2,14 @@ package com.ruoyi; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; /** * 启动程序 - * + * * @author ruoyi */ -@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +@SpringBootApplication public class RuoYiApplication { public static void main(String[] args) diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java index 7418a2cd..38913f50 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -54,10 +54,10 @@ public class CommonController response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); FileUtils.setAttachmentResponseHeader(response, realFileName); - FileUtils.writeBytes(filePath, response.getOutputStream()); + FileUtils.writeToStream(filePath, response.getOutputStream()); if (delete) { - FileUtils.deleteFile(filePath); + FileUtils.del(filePath); } } catch (Exception e) @@ -111,7 +111,7 @@ public class CommonController String downloadName = StrUtil.subAfter(downloadPath, "/",true); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); FileUtils.setAttachmentResponseHeader(response, downloadName); - FileUtils.writeBytes(downloadPath, response.getOutputStream()); + FileUtils.writeToStream(downloadPath, response.getOutputStream()); } catch (Exception e) { diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java index c3fdf83f..ab39319a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -56,7 +56,7 @@ public class SysDeptController extends BaseController { SysDept d = (SysDept) it.next(); if (d.getDeptId().intValue() == deptId - || ArrayUtils.contains(StrUtil.split(d.getAncestors(), ","), deptId + "")) + || ArrayUtils.contains(StrUtil.splitToArray(d.getAncestors(), ","), deptId + "")) { it.remove(); } diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index eb54d9c6..35901b51 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -1,21 +1,27 @@ # 数据源配置 spring: + autoconfigure: + exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure datasource: type: com.alibaba.druid.pool.DruidDataSource - driverClassName: com.mysql.cj.jdbc.Driver + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + #设置默认的数据源或者数据源组,默认值即为 master + primary: master + datasource: + # 主库数据源 + master: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true + username: root + password: root + # 从库数据源 + slave: + driverClassName: com.mysql.cj.jdbc.Driver + url: + username: + password: druid: - # 主库数据源 - master: - url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true - username: root - password: root - # 从库数据源 - slave: - # 从数据源开关/默认关闭 - enabled: false - url: - username: - password: # 初始连接数 initialSize: 5 # 最小连接池数量 @@ -35,6 +41,8 @@ spring: testWhileIdle: true testOnBorrow: false testOnReturn: false + # 注意这个值和druid原生不一致,默认启动了stat + filters: stat webStatFilter: enabled: true statViewServlet: diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 2ebdcbdc..35901b51 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -1,21 +1,27 @@ # 数据源配置 spring: + autoconfigure: + exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure datasource: type: com.alibaba.druid.pool.DruidDataSource - driverClassName: com.mysql.cj.jdbc.Driver + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + #设置默认的数据源或者数据源组,默认值即为 master + primary: master + datasource: + # 主库数据源 + master: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true + username: root + password: root + # 从库数据源 + slave: + driverClassName: com.mysql.cj.jdbc.Driver + url: + username: + password: druid: - # 主库数据源 - master: - url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true - username: root - password: root - # 从库数据源 - slave: - # 从数据源开关/默认关闭 - enabled: false - url: - username: - password: # 初始连接数 initialSize: 5 # 最小连接池数量 @@ -35,6 +41,8 @@ spring: testWhileIdle: true testOnBorrow: false testOnReturn: false + # 注意这个值和druid原生不一致,默认启动了stat + filters: stat webStatFilter: enabled: true statViewServlet: @@ -58,7 +66,7 @@ spring: # redis 配置 redis: # 地址 - host: 192.168.0.222 + host: localhost # 端口,默认为6379 port: 6379 # 数据库索引 diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 476a9333..e153d194 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -11,7 +11,7 @@ ruoyi: # 文件路径 profile: ./ruoyi/uploadPath # 获取ip地址开关 - addressEnabled: false + addressEnabled: true captcha: # 验证码开关 @@ -86,6 +86,17 @@ spring: thymeleaf: # 将系统模板放置到最前面 否则会与 springboot-admin 页面冲突 template-resolver-order: 1 + jackson: + # 日期格式化 + date-format: yyyy-MM-dd HH:mm:ss + serialization: + # 格式化输出 + indent_output: false + # 忽略无法转换的对象 + fail_on_empty_beans: false + deserialization: + # 允许对象忽略json中不存在的属性 + fail_on_unknown_properties: false # token配置 token: @@ -242,9 +253,16 @@ feign: enabled: true okhttp: enabled: true - hystrix: + circuitbreaker: enabled: true +--- # 分布式锁 lock4j 全局配置 +lock4j: + # 获取分布式锁超时时间,默认为 3000 毫秒 + acquire-timeout: 3000 + # 分布式锁的超时时间,默认为 30 毫秒 + expire: 30000 + --- # 定时任务配置 spring: quartz: diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index c6275c25..e2cbd39f 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -53,12 +53,6 @@ jackson-databind - - - com.alibaba - fastjson - - org.apache.poi @@ -152,6 +146,17 @@ redisson-spring-boot-starter + + + com.baomidou + dynamic-datasource-spring-boot-starter + + + + com.baomidou + lock4j-redisson-spring-boot-starter + + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RedisLock.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RedisLock.java deleted file mode 100644 index 7b3c912f..00000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RedisLock.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 分布式锁(注解模式,不推荐使用,最好用锁的工具类) - * - * @author shenxinquan - */ - -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface RedisLock { - - /** - * 锁过期时间 默认30秒 - */ - int expireTime() default 30; - - /** - * 锁key值 - */ - String key() default "redisLockKey"; -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java index 137969a8..a8711789 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -127,6 +127,11 @@ public class Constants */ public static final String RESOURCE_PREFIX = "/profile"; + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi://"; + /** * 资源映射路径 前缀 */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java index 317b92e2..632c0fb4 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java @@ -2,14 +2,8 @@ package com.ruoyi.common.core.controller; import cn.hutool.core.util.StrUtil; import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.utils.DateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; - -import java.beans.PropertyEditorSupport; -import java.util.Date; /** * web层通用数据处理 @@ -20,23 +14,6 @@ public class BaseController { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); - /** - * 将前台传递过来的日期格式的字符串,自动转化为Date类型 - */ - @InitBinder - public void initBinder(WebDataBinder binder) - { - // Date 类型转换 - binder.registerCustomEditor(Date.class, new PropertyEditorSupport() - { - @Override - public void setAsText(String text) - { - setValue(DateUtils.parseDate(text)); - } - }); - } - /** * 响应返回结果 * diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java index 55655bff..d66686be 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java @@ -1,6 +1,5 @@ package com.ruoyi.common.core.domain; -import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @@ -12,7 +11,7 @@ import java.util.Map; /** * Entity基类 - * + * * @author ruoyi */ @@ -30,14 +29,12 @@ public class BaseEntity implements Serializable private String createBy; /** 创建时间 */ - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; /** 更新者 */ private String updateBy; /** 更新时间 */ - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date updateTime; /** 备注 */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/MybatisPlusRedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java similarity index 98% rename from ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/MybatisPlusRedisCache.java rename to ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java index 6ff6a982..0e21d70e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/MybatisPlusRedisCache.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java @@ -1,4 +1,4 @@ -package com.ruoyi.common.core.mybatisplus; +package com.ruoyi.common.core.mybatisplus.cache; import cn.hutool.extra.spring.SpringUtil; import com.ruoyi.common.core.redis.RedisCache; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/BaseMapperPlus.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/BaseMapperPlus.java similarity index 55% rename from ruoyi-common/src/main/java/com/ruoyi/common/core/page/BaseMapperPlus.java rename to ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/BaseMapperPlus.java index ebba1b7a..cf51c90e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/BaseMapperPlus.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/BaseMapperPlus.java @@ -1,6 +1,9 @@ -package com.ruoyi.common.core.page; +package com.ruoyi.common.core.mybatisplus.core; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; /** * 自定义 Mapper 接口, 实现 自定义扩展 @@ -9,4 +12,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; * @since 2021-05-13 */ public interface BaseMapperPlus extends BaseMapper { + + int insertAll(@Param("list") Collection batchList); + } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/IServicePlus.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/IServicePlus.java similarity index 93% rename from ruoyi-common/src/main/java/com/ruoyi/common/core/page/IServicePlus.java rename to ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/IServicePlus.java index aeaee0b5..21b0ecce 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/IServicePlus.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/IServicePlus.java @@ -1,9 +1,10 @@ -package com.ruoyi.common.core.page; +package com.ruoyi.common.core.mybatisplus.core; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.page.PagePlus; import java.io.Serializable; import java.util.Collection; @@ -182,7 +183,6 @@ public interface IServicePlus extends IService { * * @param page 翻页对象 * @param queryWrapper 实体对象封装操作类 - * @param kClass vo类型 */ default PagePlus pageVo(PagePlus page, Wrapper queryWrapper, Class kClass) { PagePlus result = getBaseMapper().selectPage(page, queryWrapper); @@ -210,7 +210,6 @@ public interface IServicePlus extends IService { * 无条件翻页查询 * * @param page 翻页对象 - * @param kClass vo类型 */ default PagePlus pageVo(PagePlus page, Class kClass) { return pageVo(page, Wrappers.emptyWrapper(), kClass); @@ -226,5 +225,21 @@ public interface IServicePlus extends IService { return pageVo(page, Wrappers.emptyWrapper(), convertor); } + @Override + default boolean saveBatch(Collection entityList) { + return saveBatch(entityList, DEFAULT_BATCH_SIZE); + } + + @Override + default boolean saveOrUpdateBatch(Collection entityList) { + return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE); + } + + @Override + default boolean updateBatchById(Collection entityList) { + return updateBatchById(entityList, DEFAULT_BATCH_SIZE); + } + + boolean saveAll(Collection entityList); } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/ServicePlusImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/ServicePlusImpl.java new file mode 100644 index 00000000..77b9c286 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/ServicePlusImpl.java @@ -0,0 +1,90 @@ +package com.ruoyi.common.core.mybatisplus.core; + +import com.baomidou.mybatisplus.core.toolkit.ClassUtils; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ResolvableType; + +import java.util.Collection; + +/** + * IServicePlus 实现类 + * + * @author Lion Li + */ +@Slf4j +@SuppressWarnings("unchecked") +public class ServicePlusImpl, T> extends ServiceImpl implements IServicePlus { + + @Autowired + protected M baseMapper; + + @Override + public M getBaseMapper() { + return baseMapper; + } + + + protected Class entityClass = currentModelClass(); + + @Override + public Class getEntityClass() { + return entityClass; + } + + protected Class mapperClass = currentMapperClass(); + + @Override + protected Class currentMapperClass() { + return (Class) this.getResolvableType().as(ServicePlusImpl.class).getGeneric(0).getType(); + } + + @Override + protected Class currentModelClass() { + return (Class) this.getResolvableType().as(ServicePlusImpl.class).getGeneric(1).getType(); + } + + @Override + protected ResolvableType getResolvableType() { + return ResolvableType.forClass(ClassUtils.getUserClass(getClass())); + } + + /** + * 单条执行性能差 + * + * {@link #saveAll(Collection)} + */ + @Deprecated + @Override + public boolean saveBatch(Collection entityList, int batchSize) { + return super.saveBatch(entityList, batchSize); + } + + @Override + public boolean saveOrUpdate(T entity) { + return super.saveOrUpdate(entity); + } + + /** + * 单条执行性能差 + * + * {@link #saveAll(Collection)} + */ + @Deprecated + @Override + public boolean saveOrUpdateBatch(Collection entityList, int batchSize) { + return super.saveOrUpdateBatch(entityList, batchSize); + } + + @Override + public boolean updateBatchById(Collection entityList, int batchSize) { + return super.updateBatchById(entityList, batchSize); + } + + @Override + public boolean saveAll(Collection entityList) { + return baseMapper.insertAll(entityList) == entityList.size(); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/methods/InsertAll.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/methods/InsertAll.java new file mode 100644 index 00000000..9c8c0f74 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/methods/InsertAll.java @@ -0,0 +1,52 @@ +package com.ruoyi.common.core.mybatisplus.methods; + +import cn.hutool.core.util.StrUtil; +import org.apache.ibatis.executor.keygen.NoKeyGenerator; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlSource; + +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.metadata.TableInfo; + +/** + * 单sql批量插入 + * + * @author Lion Li + */ +public class InsertAll extends AbstractMethod { + + @Override + public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { + final String sql = ""; + final String fieldSql = prepareFieldSql(tableInfo); + final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo); + final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql); + SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass); + return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, new NoKeyGenerator(), null, null); + } + + private String prepareFieldSql(TableInfo tableInfo) { + StringBuilder fieldSql = new StringBuilder(); + if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) { + fieldSql.append(tableInfo.getKeyColumn()).append(","); + } + tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(",")); + fieldSql.delete(fieldSql.length() - 1, fieldSql.length()); + fieldSql.insert(0, "("); + fieldSql.append(")"); + return fieldSql.toString(); + } + + private String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) { + final StringBuilder valueSql = new StringBuilder(); + valueSql.append(""); + if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) { + valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},"); + } + tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},")); + valueSql.delete(valueSql.length() - 1, valueSql.length()); + valueSql.append(""); + return valueSql.toString(); + } +} + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisLockManager.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisLockManager.java deleted file mode 100644 index 0688bb1c..00000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisLockManager.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.ruoyi.common.core.redis; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Validator; -import cn.hutool.core.util.StrUtil; -import org.redisson.api.RCountDownLatch; -import org.redisson.api.RLock; -import org.redisson.api.RedissonClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.concurrent.TimeUnit; - -/** - * redis 锁管理类 - * - * @author shenxinquan - */ -@Component -public class RedisLockManager { - - @Autowired - private RedissonClient redissonClient; - - /** - * 通用锁 - */ - private final static Integer BASE_LOCK = 1; - - /** - * 公平锁 - */ - private final static Integer FAIR_LOCK = 2; - - /** - * 存放当前线程获取锁的类型 - */ - private final ThreadLocal threadLocal = new ThreadLocal<>(); - - /** - * 获取锁 - */ - private RLock getLock(String key, Integer lockType) { - Assert.isTrue(StrUtil.isNotBlank(key), "key不能为空"); - threadLocal.set(lockType); - RLock lock; - if (BASE_LOCK.equals(lockType)) { - lock = redissonClient.getLock(key); - } else if (FAIR_LOCK.equals(lockType)) { - lock = redissonClient.getFairLock(key); - } else { - throw new RuntimeException("锁不存在!"); - } - return lock; - } - - /** - * 获取锁(不用设置超时时间,一直等待) - */ - public boolean getLock(String key) { - RLock lock = getLock(key, BASE_LOCK); - return lock.tryLock(); - } - - /** - * 设置过期时间 - * - * @param key - * @param time 过期时间 - * @param expireUnit 时间单位 - */ - public boolean getLock(String key, long time, TimeUnit expireUnit) { - Assert.isTrue(time > 0, "过期时间必须大于0"); - Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空"); - RLock lock = getLock(key, BASE_LOCK); - try { - return lock.tryLock(time, expireUnit); - } catch (InterruptedException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 设置过期时间 - * - * @param key - * @param waitTime 获取锁等待时间 - * @param leaseTime 保留锁的时间 - * @param expireUnit 时间单位 - */ - public boolean getLock(String key, long waitTime, long leaseTime, TimeUnit expireUnit) { - Assert.isTrue(waitTime > 0, "获取锁等待时间必须大于0"); - Assert.isTrue(leaseTime > 0, "保留锁的时间必须大于0"); - Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空"); - RLock lock = getLock(key, BASE_LOCK); - try { - return lock.tryLock(waitTime, leaseTime, expireUnit); - } catch (InterruptedException e) { - e.printStackTrace(); - return false; - } - } - - - /** - * 获取计数器锁 - * - * @param key - * @param count countDownLatch 的数量 - */ - public RCountDownLatch getCountDownLatch(String key, long count) { - Assert.isTrue(count >= 0, "count数量必须大于等于0"); - RCountDownLatch rCountDownLatch = redissonClient.getCountDownLatch(key); - rCountDownLatch.trySetCount(count); - return rCountDownLatch; - } - - /** - * 获取公平锁 - * - * @param key - * @param waitTime 获取锁等待时间 - * @param leaseTime 持有锁的时间 - * @param expireUnit 时间单位 - * @return - * @throws InterruptedException - */ - public boolean getFairLock(String key, long waitTime, long leaseTime, TimeUnit expireUnit) { - Assert.isTrue(waitTime > 0, "获取锁等待时间必须大于0"); - Assert.isTrue(leaseTime > 0, "保留锁的时间必须大于0"); - Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空"); - RLock lock = getLock(key, FAIR_LOCK); - try { - return lock.tryLock(waitTime, leaseTime, expireUnit); - } catch (InterruptedException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 获取公平锁 - * - * @param key - * @param leaseTime 持有锁的时间 - * @param expireUnit 时间单位 - */ - public boolean getFairLock(String key, long leaseTime, TimeUnit expireUnit) { - Assert.isTrue(leaseTime > 0, "保留锁的时间必须大于0"); - Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空"); - RLock lock = getLock(key, FAIR_LOCK); - try { - return lock.tryLock(leaseTime, expireUnit); - } catch (InterruptedException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 释放锁(统一释放) - */ - public void unLock(String key) { - Integer lockType = threadLocal.get(); - RLock lock = getLock(key, lockType); - lock.unlock(); - threadLocal.remove(); - } -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java index 0d945be5..d5f54653 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java @@ -1,19 +1,25 @@ package com.ruoyi.common.enums; +import lombok.AllArgsConstructor; +import lombok.Getter; + /** * 数据源 - * - * @author ruoyi + * + * @author Lion Li */ -public enum DataSourceType -{ - /** - * 主库 - */ - MASTER, +@AllArgsConstructor +public enum DataSourceType { + /** + * 主库 + */ + MASTER("master"), - /** - * 从库 - */ - SLAVE + /** + * 从库 + */ + SLAVE("slave"); + + @Getter + private final String source; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java index caacba63..74659bfd 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java @@ -1,75 +1,77 @@ -package com.ruoyi.common.filter; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import com.ruoyi.common.utils.http.HttpHelper; - -/** - * 构建可重复读取inputStream的request - * - * @author ruoyi - */ -public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper -{ - private final byte[] body; - - public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException - { - super(request); - request.setCharacterEncoding("UTF-8"); - response.setCharacterEncoding("UTF-8"); - - body = HttpHelper.getBodyString(request).getBytes("UTF-8"); - } - - @Override - public BufferedReader getReader() throws IOException - { - return new BufferedReader(new InputStreamReader(getInputStream())); - } - - @Override - public ServletInputStream getInputStream() throws IOException - { - final ByteArrayInputStream bais = new ByteArrayInputStream(body); - return new ServletInputStream() - { - @Override - public int read() throws IOException - { - return bais.read(); - } - - @Override - public int available() throws IOException - { - return body.length; - } - - @Override - public boolean isFinished() - { - return false; - } - - @Override - public boolean isReady() - { - return false; - } - - @Override - public void setReadListener(ReadListener readListener) - { - - } - }; - } -} +package com.ruoyi.common.filter; + +import cn.hutool.core.io.IoUtil; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + * 构建可重复读取inputStream的request + * + * @author ruoyi + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding("UTF-8"); + response.setCharacterEncoding("UTF-8"); + + body = IoUtil.readUtf8(request.getInputStream()).getBytes(StandardCharsets.UTF_8); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java index 0170e1d7..4d36a92e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java @@ -1,9 +1,9 @@ package com.ruoyi.common.filter; +import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HtmlUtil; -import org.apache.commons.io.IOUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -13,6 +13,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; /** * XSS过滤处理 @@ -57,7 +58,7 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper } // 为空,直接返回 - String json = IOUtils.toString(super.getInputStream(), "utf-8"); + String json = IoUtil.read(super.getInputStream(), StandardCharsets.UTF_8); if (Validator.isEmpty(json)) { return super.getInputStream(); @@ -65,7 +66,8 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper // xss过滤 json = HtmlUtil.cleanHtmlTag(json).trim(); - final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8")); + + final ByteArrayInputStream bis = IoUtil.toStream(json, StandardCharsets.UTF_8); return new ServletInputStream() { @Override diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java new file mode 100644 index 00000000..ae6cc11b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java @@ -0,0 +1,101 @@ +package com.ruoyi.common.utils; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * JSON 工具类 + * + * @author 芋道源码 + */ +public class JsonUtils { + + private static ObjectMapper objectMapper = new ObjectMapper(); + + /** + * 初始化 objectMapper 属性 + *

+ * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean + * + * @param objectMapper ObjectMapper 对象 + */ + public static void init(ObjectMapper objectMapper) { + JsonUtils.objectMapper = objectMapper; + } + + public static String toJsonString(Object object) { + if (Validator.isEmpty(object)) { + return null; + } + try { + return objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return null; + } + try { + return objectMapper.readValue(text, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(byte[] bytes, Class clazz) { + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + try { + return objectMapper.readValue(bytes, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, TypeReference typeReference) { + if (StrUtil.isBlank(text)) { + return null; + } + try { + return objectMapper.readValue(text, typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Map parseMap(String text) { + if (StrUtil.isBlank(text)) { + return null; + } + try { + return objectMapper.readValue(text, new TypeReference>() {}); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static List parseArray(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return new ArrayList<>(); + } + try { + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java index d5b72f77..bb3d3e2d 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java @@ -70,20 +70,31 @@ public class PageUtils { return page; } + public static Page buildPage() { + return buildPage(null, null); + } + /** * 构建 MP 普通分页对象 * @param domain 实体 * @return 分页对象 */ - public static Page buildPage() { + public static Page buildPage(String defaultOrderByColumn, String defaultIsAsc) { Integer pageNum = ServletUtils.getParameterToInt(PAGE_NUM, DEFAULT_PAGE_NUM); Integer pageSize = ServletUtils.getParameterToInt(PAGE_SIZE, DEFAULT_PAGE_SIZE); - String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN); - String isAsc = ServletUtils.getParameter(IS_ASC); + String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN, defaultOrderByColumn); + String isAsc = ServletUtils.getParameter(IS_ASC, defaultIsAsc); + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) { + isAsc = "asc"; + } else if ("descending".equals(isAsc)) { + isAsc = "desc"; + } Page page = new Page<>(pageNum, pageSize); if (StrUtil.isNotBlank(orderByColumn)) { String orderBy = SqlUtil.escapeOrderBySql(orderByColumn); - if ("asc".equals(isAsc)) { + orderBy = StrUtil.toUnderlineCase(orderBy); + if ("asc".equals(isAsc)) { page.addOrder(OrderItem.asc(orderBy)); } else if ("desc".equals(isAsc)) { page.addOrder(OrderItem.desc(orderBy)); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java index 0535cf2c..15942916 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java @@ -2,6 +2,9 @@ package com.ruoyi.common.utils; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -10,129 +13,118 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; +import java.nio.charset.StandardCharsets; /** * 客户端工具类 - * + * * @author ruoyi */ -public class ServletUtils -{ - /** - * 获取String参数 - */ - public static String getParameter(String name) - { - return getRequest().getParameter(name); - } +public class ServletUtils extends ServletUtil { + /** + * 获取String参数 + */ + public static String getParameter(String name) { + return getRequest().getParameter(name); + } - /** - * 获取String参数 - */ - public static String getParameter(String name, String defaultValue) - { - return Convert.toStr(getRequest().getParameter(name), defaultValue); - } + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } - /** - * 获取Integer参数 - */ - public static Integer getParameterToInt(String name) - { - return Convert.toInt(getRequest().getParameter(name)); - } + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) { + return Convert.toInt(getRequest().getParameter(name)); + } - /** - * 获取Integer参数 - */ - public static Integer getParameterToInt(String name, Integer defaultValue) - { - return Convert.toInt(getRequest().getParameter(name), defaultValue); - } + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } - /** - * 获取request - */ - public static HttpServletRequest getRequest() - { - return getRequestAttributes().getRequest(); - } + /** + * 获取request + */ + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } - /** - * 获取response - */ - public static HttpServletResponse getResponse() - { - return getRequestAttributes().getResponse(); - } + /** + * 获取response + */ + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } - /** - * 获取session - */ - public static HttpSession getSession() - { - return getRequest().getSession(); - } + /** + * 获取session + */ + public static HttpSession getSession() { + return getRequest().getSession(); + } - public static ServletRequestAttributes getRequestAttributes() - { - RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); - return (ServletRequestAttributes) attributes; - } + public static ServletRequestAttributes getRequestAttributes() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } - /** - * 将字符串渲染到客户端 - * - * @param response 渲染对象 - * @param string 待渲染的字符串 - * @return null - */ - public static String renderString(HttpServletResponse response, String string) - { - try - { - response.setStatus(200); - response.setContentType("application/json"); - response.setCharacterEncoding("utf-8"); - response.getWriter().print(string); - } - catch (IOException e) - { - e.printStackTrace(); - } - return null; - } + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + * @return null + */ + public static String renderString(HttpServletResponse response, String string) { + try { + response.setStatus(HttpStatus.HTTP_OK); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + response.getWriter().print(string); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } - /** - * 是否是Ajax异步请求 - * - * @param request - */ - public static boolean isAjaxRequest(HttpServletRequest request) - { - String accept = request.getHeader("accept"); - if (accept != null && accept.indexOf("application/json") != -1) - { - return true; - } + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) { - String xRequestedWith = request.getHeader("X-Requested-With"); - if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) - { - return true; - } + String accept = request.getHeader("accept"); + if (accept != null && accept.indexOf("application/json") != -1) { + return true; + } - String uri = request.getRequestURI(); - if (StrUtil.equalsAnyIgnoreCase(uri, ".json", ".xml")) - { - return true; - } + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) { + return true; + } + + String uri = request.getRequestURI(); + if (StrUtil.equalsAnyIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + String ajax = request.getParameter("__ajax"); + if (StrUtil.equalsAnyIgnoreCase(ajax, "json", "xml")) { + return true; + } + return false; + } + + public static String getClientIP() { + return getClientIP(getRequest()); + } - String ajax = request.getParameter("__ajax"); - if (StrUtil.equalsAnyIgnoreCase(ajax, "json", "xml")) - { - return true; - } - return false; - } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java index 587b2bf5..bc953de0 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -1,5 +1,6 @@ package com.ruoyi.common.utils.file; +import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; @@ -11,91 +12,16 @@ import java.nio.charset.StandardCharsets; /** * 文件处理工具类 - * + * * @author ruoyi */ -public class FileUtils extends org.apache.commons.io.FileUtils +public class FileUtils extends FileUtil { public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; - /** - * 输出指定文件的byte数组 - * - * @param filePath 文件路径 - * @param os 输出流 - * @return - */ - public static void writeBytes(String filePath, OutputStream os) throws IOException - { - FileInputStream fis = null; - try - { - File file = new File(filePath); - if (!file.exists()) - { - throw new FileNotFoundException(filePath); - } - fis = new FileInputStream(file); - byte[] b = new byte[1024]; - int length; - while ((length = fis.read(b)) > 0) - { - os.write(b, 0, length); - } - } - catch (IOException e) - { - throw e; - } - finally - { - if (os != null) - { - try - { - os.close(); - } - catch (IOException e1) - { - e1.printStackTrace(); - } - } - if (fis != null) - { - try - { - fis.close(); - } - catch (IOException e1) - { - e1.printStackTrace(); - } - } - } - } - - /** - * 删除文件 - * - * @param filePath 文件 - * @return - */ - public static boolean deleteFile(String filePath) - { - boolean flag = false; - File file = new File(filePath); - // 路径为文件且不为空则进行删除 - if (file.isFile() && file.exists()) - { - file.delete(); - flag = true; - } - return flag; - } - /** * 文件名称验证 - * + * * @param filename 文件名称 * @return true 正常 false 非法 */ @@ -130,7 +56,7 @@ public class FileUtils extends org.apache.commons.io.FileUtils /** * 下载文件名重新编码 - * + * * @param request 请求对象 * @param fileName 文件名 * @return 编码后的文件名 @@ -142,7 +68,7 @@ public class FileUtils extends org.apache.commons.io.FileUtils if (agent.contains("MSIE")) { // IE浏览器 - filename = URLEncoder.encode(filename, "utf-8"); + filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()); filename = filename.replace("+", " "); } else if (agent.contains("Firefox")) @@ -153,12 +79,12 @@ public class FileUtils extends org.apache.commons.io.FileUtils else if (agent.contains("Chrome")) { // google浏览器 - filename = URLEncoder.encode(filename, "utf-8"); + filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()); } else { // 其它浏览器 - filename = URLEncoder.encode(filename, "utf-8"); + filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()); } return filename; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java deleted file mode 100644 index 430daf86..00000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.ruoyi.common.utils.http; - -import cn.hutool.core.exceptions.ExceptionUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.servlet.ServletRequest; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.Charset; - -/** - * 通用http工具封装 - * - * @author ruoyi - */ -public class HttpHelper -{ - private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); - - public static String getBodyString(ServletRequest request) - { - StringBuilder sb = new StringBuilder(); - BufferedReader reader = null; - try (InputStream inputStream = request.getInputStream()) - { - reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); - String line = ""; - while ((line = reader.readLine()) != null) - { - sb.append(line); - } - } - catch (IOException e) - { - LOGGER.warn("getBodyString出现问题!"); - } - finally - { - if (reader != null) - { - try - { - reader.close(); - } - catch (IOException e) - { - LOGGER.error(ExceptionUtil.getMessage(e)); - } - } - } - return sb.toString(); - } -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java deleted file mode 100644 index 9aee92ef..00000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java +++ /dev/null @@ -1,262 +0,0 @@ -package com.ruoyi.common.utils.http; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.net.ConnectException; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.URLConnection; -import java.security.cert.X509Certificate; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.ruoyi.common.constant.Constants; - -/** - * 通用http发送方法 - * - * @author ruoyi - */ -public class HttpUtils -{ - private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); - - /** - * 向指定 URL 发送GET方法的请求 - * - * @param url 发送请求的 URL - * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 - * @return 所代表远程资源的响应结果 - */ - public static String sendGet(String url, String param) - { - return sendGet(url, param, Constants.UTF8); - } - - /** - * 向指定 URL 发送GET方法的请求 - * - * @param url 发送请求的 URL - * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 - * @param contentType 编码类型 - * @return 所代表远程资源的响应结果 - */ - public static String sendGet(String url, String param, String contentType) - { - StringBuilder result = new StringBuilder(); - BufferedReader in = null; - try - { - String urlNameString = url + "?" + param; - log.info("sendGet - {}", urlNameString); - URL realUrl = new URL(urlNameString); - URLConnection connection = realUrl.openConnection(); - connection.setRequestProperty("accept", "*/*"); - connection.setRequestProperty("connection", "Keep-Alive"); - connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); - connection.connect(); - in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); - String line; - while ((line = in.readLine()) != null) - { - result.append(line); - } - log.info("recv - {}", result); - } - catch (ConnectException e) - { - log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); - } - catch (SocketTimeoutException e) - { - log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); - } - catch (IOException e) - { - log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); - } - catch (Exception e) - { - log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); - } - finally - { - try - { - if (in != null) - { - in.close(); - } - } - catch (Exception ex) - { - log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); - } - } - return result.toString(); - } - - /** - * 向指定 URL 发送POST方法的请求 - * - * @param url 发送请求的 URL - * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 - * @return 所代表远程资源的响应结果 - */ - public static String sendPost(String url, String param) - { - PrintWriter out = null; - BufferedReader in = null; - StringBuilder result = new StringBuilder(); - try - { - String urlNameString = url; - log.info("sendPost - {}", urlNameString); - URL realUrl = new URL(urlNameString); - URLConnection conn = realUrl.openConnection(); - conn.setRequestProperty("accept", "*/*"); - conn.setRequestProperty("connection", "Keep-Alive"); - conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); - conn.setRequestProperty("Accept-Charset", "utf-8"); - conn.setRequestProperty("contentType", "utf-8"); - conn.setDoOutput(true); - conn.setDoInput(true); - out = new PrintWriter(conn.getOutputStream()); - out.print(param); - out.flush(); - in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8")); - String line; - while ((line = in.readLine()) != null) - { - result.append(line); - } - log.info("recv - {}", result); - } - catch (ConnectException e) - { - log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); - } - catch (SocketTimeoutException e) - { - log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); - } - catch (IOException e) - { - log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); - } - catch (Exception e) - { - log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); - } - finally - { - try - { - if (out != null) - { - out.close(); - } - if (in != null) - { - in.close(); - } - } - catch (IOException ex) - { - log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); - } - } - return result.toString(); - } - - public static String sendSSLPost(String url, String param) - { - StringBuilder result = new StringBuilder(); - String urlNameString = url + "?" + param; - try - { - log.info("sendSSLPost - {}", urlNameString); - SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); - URL console = new URL(urlNameString); - HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); - conn.setRequestProperty("accept", "*/*"); - conn.setRequestProperty("connection", "Keep-Alive"); - conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); - conn.setRequestProperty("Accept-Charset", "utf-8"); - conn.setRequestProperty("contentType", "utf-8"); - conn.setDoOutput(true); - conn.setDoInput(true); - - conn.setSSLSocketFactory(sc.getSocketFactory()); - conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); - conn.connect(); - InputStream is = conn.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String ret = ""; - while ((ret = br.readLine()) != null) - { - if (ret != null && !"".equals(ret.trim())) - { - result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8")); - } - } - log.info("recv - {}", result); - conn.disconnect(); - br.close(); - } - catch (ConnectException e) - { - log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); - } - catch (SocketTimeoutException e) - { - log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); - } - catch (IOException e) - { - log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); - } - catch (Exception e) - { - log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); - } - return result.toString(); - } - - private static class TrustAnyTrustManager implements X509TrustManager - { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - { - } - - @Override - public X509Certificate[] getAcceptedIssuers() - { - return new X509Certificate[] {}; - } - } - - private static class TrustAnyHostnameVerifier implements HostnameVerifier - { - @Override - public boolean verify(String hostname, SSLSession session) - { - return true; - } - } -} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java index 4805d258..d6c262a2 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -1,56 +1,55 @@ package com.ruoyi.common.utils.ip; +import cn.hutool.core.net.NetUtil; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSONObject; +import cn.hutool.http.HtmlUtil; +import cn.hutool.http.HttpUtil; import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.utils.http.HttpUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.ruoyi.common.utils.JsonUtils; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; /** * 获取地址类 - * + * * @author ruoyi */ -public class AddressUtils -{ - private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); +@Slf4j +public class AddressUtils { - // IP地址查询 - public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; - // 未知地址 - public static final String UNKNOWN = "XX XX"; + // 未知地址 + public static final String UNKNOWN = "XX XX"; - public static String getRealAddressByIP(String ip) - { - String address = UNKNOWN; - // 内网不查询 - if (IpUtils.internalIp(ip)) - { - return "内网IP"; - } - if (RuoYiConfig.isAddressEnabled()) - { - try - { - String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); - if (StrUtil.isEmpty(rspStr)) - { - log.error("获取地理位置异常 {}", ip); - return UNKNOWN; - } - JSONObject obj = JSONObject.parseObject(rspStr); - String region = obj.getString("pro"); - String city = obj.getString("city"); - return String.format("%s %s", region, city); - } - catch (Exception e) - { - log.error("获取地理位置异常 {}", ip); - } - } - return address; - } + public static String getRealAddressByIP(String ip) { + String address = UNKNOWN; + // 内网不查询 + ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); + if (NetUtil.isInnerIP(ip)) { + return "内网IP"; + } + if (RuoYiConfig.isAddressEnabled()) { + try { + String rspStr = HttpUtil.createGet(IP_URL) + .body("ip=" + ip + "&json=true", Constants.GBK) + .execute() + .body(); + if (StrUtil.isEmpty(rspStr)) { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + Map obj = JsonUtils.parseMap(rspStr); + String region = obj.get("pro"); + String city = obj.get("city"); + return String.format("%s %s", region, city); + } catch (Exception e) { + log.error("获取地理位置异常 {}", ip); + } + } + return address; + } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java deleted file mode 100644 index c46c69b0..00000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.ruoyi.common.utils.ip; - -import cn.hutool.core.lang.Validator; -import cn.hutool.http.HtmlUtil; - -import javax.servlet.http.HttpServletRequest; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * 获取IP方法 - * - * @author ruoyi - */ -public class IpUtils -{ - public static String getIpAddr(HttpServletRequest request) - { - if (request == null) - { - return "unknown"; - } - String ip = request.getHeader("x-forwarded-for"); - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getHeader("Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getHeader("X-Forwarded-For"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getHeader("WL-Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getHeader("X-Real-IP"); - } - - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getRemoteAddr(); - } - return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); - } - - public static boolean internalIp(String ip) - { - byte[] addr = textToNumericFormatV4(ip); - return internalIp(addr) || "127.0.0.1".equals(ip); - } - - private static boolean internalIp(byte[] addr) - { - if (Validator.isNull(addr) || addr.length < 2) - { - return true; - } - final byte b0 = addr[0]; - final byte b1 = addr[1]; - // 10.x.x.x/8 - final byte SECTION_1 = 0x0A; - // 172.16.x.x/12 - final byte SECTION_2 = (byte) 0xAC; - final byte SECTION_3 = (byte) 0x10; - final byte SECTION_4 = (byte) 0x1F; - // 192.168.x.x/16 - final byte SECTION_5 = (byte) 0xC0; - final byte SECTION_6 = (byte) 0xA8; - switch (b0) - { - case SECTION_1: - return true; - case SECTION_2: - if (b1 >= SECTION_3 && b1 <= SECTION_4) - { - return true; - } - case SECTION_5: - switch (b1) - { - case SECTION_6: - return true; - } - default: - return false; - } - } - - /** - * 将IPv4地址转换成字节 - * - * @param text IPv4地址 - * @return byte 字节 - */ - public static byte[] textToNumericFormatV4(String text) - { - if (text.length() == 0) - { - return null; - } - - byte[] bytes = new byte[4]; - String[] elements = text.split("\\.", -1); - try - { - long l; - int i; - switch (elements.length) - { - case 1: - l = Long.parseLong(elements[0]); - if ((l < 0L) || (l > 4294967295L)) { - return null; - } - bytes[0] = (byte) (int) (l >> 24 & 0xFF); - bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); - bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); - bytes[3] = (byte) (int) (l & 0xFF); - break; - case 2: - l = Integer.parseInt(elements[0]); - if ((l < 0L) || (l > 255L)) { - return null; - } - bytes[0] = (byte) (int) (l & 0xFF); - l = Integer.parseInt(elements[1]); - if ((l < 0L) || (l > 16777215L)) { - return null; - } - bytes[1] = (byte) (int) (l >> 16 & 0xFF); - bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); - bytes[3] = (byte) (int) (l & 0xFF); - break; - case 3: - for (i = 0; i < 2; ++i) - { - l = Integer.parseInt(elements[i]); - if ((l < 0L) || (l > 255L)) { - return null; - } - bytes[i] = (byte) (int) (l & 0xFF); - } - l = Integer.parseInt(elements[2]); - if ((l < 0L) || (l > 65535L)) { - return null; - } - bytes[2] = (byte) (int) (l >> 8 & 0xFF); - bytes[3] = (byte) (int) (l & 0xFF); - break; - case 4: - for (i = 0; i < 4; ++i) - { - l = Integer.parseInt(elements[i]); - if ((l < 0L) || (l > 255L)) { - return null; - } - bytes[i] = (byte) (int) (l & 0xFF); - } - break; - default: - return null; - } - } - catch (NumberFormatException e) - { - return null; - } - return bytes; - } - - public static String getHostIp() - { - try - { - return InetAddress.getLocalHost().getHostAddress(); - } - catch (UnknownHostException e) - { - } - return "127.0.0.1"; - } - - public static String getHostName() - { - try - { - return InetAddress.getLocalHost().getHostName(); - } - catch (UnknownHostException e) - { - } - return "未知"; - } -} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java index a3b3a764..c5d17d8b 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java @@ -1,406 +1,54 @@ -package com.ruoyi.common.utils.reflect; - -import cn.hutool.core.convert.Convert; -import com.ruoyi.common.utils.DateUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.apache.poi.ss.usermodel.DateUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.*; -import java.util.Date; - -/** - * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. - * - * @author ruoyi - */ -@SuppressWarnings("rawtypes") -public class ReflectUtils -{ - private static final String SETTER_PREFIX = "set"; - - private static final String GETTER_PREFIX = "get"; - - private static final String CGLIB_CLASS_SEPARATOR = "$$"; - - private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); - - /** - * 调用Getter方法. - * 支持多级,如:对象名.对象名.方法 - */ - @SuppressWarnings("unchecked") - public static E invokeGetter(Object obj, String propertyName) - { - Object object = obj; - for (String name : StringUtils.split(propertyName, ".")) - { - String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); - object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); - } - return (E) object; - } - - /** - * 调用Setter方法, 仅匹配方法名。 - * 支持多级,如:对象名.对象名.方法 - */ - public static void invokeSetter(Object obj, String propertyName, E value) - { - Object object = obj; - String[] names = StringUtils.split(propertyName, "."); - for (int i = 0; i < names.length; i++) - { - if (i < names.length - 1) - { - String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); - object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); - } - else - { - String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); - invokeMethodByName(object, setterMethodName, new Object[] { value }); - } - } - } - - /** - * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. - */ - @SuppressWarnings("unchecked") - public static E getFieldValue(final Object obj, final String fieldName) - { - Field field = getAccessibleField(obj, fieldName); - if (field == null) - { - logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); - return null; - } - E result = null; - try - { - result = (E) field.get(obj); - } - catch (IllegalAccessException e) - { - logger.error("不可能抛出的异常{}", e.getMessage()); - } - return result; - } - - /** - * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. - */ - public static void setFieldValue(final Object obj, final String fieldName, final E value) - { - Field field = getAccessibleField(obj, fieldName); - if (field == null) - { - // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); - logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); - return; - } - try - { - field.set(obj, value); - } - catch (IllegalAccessException e) - { - logger.error("不可能抛出的异常: {}", e.getMessage()); - } - } - - /** - * 直接调用对象方法, 无视private/protected修饰符. - * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. - * 同时匹配方法名+参数类型, - */ - @SuppressWarnings("unchecked") - public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, - final Object[] args) - { - if (obj == null || methodName == null) - { - return null; - } - Method method = getAccessibleMethod(obj, methodName, parameterTypes); - if (method == null) - { - logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); - return null; - } - try - { - return (E) method.invoke(obj, args); - } - catch (Exception e) - { - String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; - throw convertReflectionExceptionToUnchecked(msg, e); - } - } - - /** - * 直接调用对象方法, 无视private/protected修饰符, - * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. - * 只匹配函数名,如果有多个同名函数调用第一个。 - */ - @SuppressWarnings("unchecked") - public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) - { - Method method = getAccessibleMethodByName(obj, methodName, args.length); - if (method == null) - { - // 如果为空不报错,直接返回空。 - logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); - return null; - } - try - { - // 类型转换(将参数数据类型转换为目标方法参数类型) - Class[] cs = method.getParameterTypes(); - for (int i = 0; i < cs.length; i++) - { - if (args[i] != null && !args[i].getClass().equals(cs[i])) - { - if (cs[i] == String.class) - { - args[i] = Convert.toStr(args[i]); - if (StringUtils.endsWith((String) args[i], ".0")) - { - args[i] = StringUtils.substringBefore((String) args[i], ".0"); - } - } - else if (cs[i] == Integer.class) - { - args[i] = Convert.toInt(args[i]); - } - else if (cs[i] == Long.class) - { - args[i] = Convert.toLong(args[i]); - } - else if (cs[i] == Double.class) - { - args[i] = Convert.toDouble(args[i]); - } - else if (cs[i] == Float.class) - { - args[i] = Convert.toFloat(args[i]); - } - else if (cs[i] == Date.class) - { - if (args[i] instanceof String) - { - args[i] = DateUtils.parseDate(args[i]); - } - else - { - args[i] = DateUtil.getJavaDate((Double) args[i]); - } - } - else if (cs[i] == boolean.class || cs[i] == Boolean.class) - { - args[i] = Convert.toBool(args[i]); - } - } - } - return (E) method.invoke(obj, args); - } - catch (Exception e) - { - String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; - throw convertReflectionExceptionToUnchecked(msg, e); - } - } - - /** - * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. - * 如向上转型到Object仍无法找到, 返回null. - */ - public static Field getAccessibleField(final Object obj, final String fieldName) - { - // 为空不报错。直接返回 null - if (obj == null) - { - return null; - } - Validate.notBlank(fieldName, "fieldName can't be blank"); - for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) - { - try - { - Field field = superClass.getDeclaredField(fieldName); - makeAccessible(field); - return field; - } - catch (NoSuchFieldException e) - { - continue; - } - } - return null; - } - - /** - * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. - * 如向上转型到Object仍无法找到, 返回null. - * 匹配函数名+参数类型。 - * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) - */ - public static Method getAccessibleMethod(final Object obj, final String methodName, - final Class... parameterTypes) - { - // 为空不报错。直接返回 null - if (obj == null) - { - return null; - } - Validate.notBlank(methodName, "methodName can't be blank"); - for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) - { - try - { - Method method = searchType.getDeclaredMethod(methodName, parameterTypes); - makeAccessible(method); - return method; - } - catch (NoSuchMethodException e) - { - continue; - } - } - return null; - } - - /** - * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. - * 如向上转型到Object仍无法找到, 返回null. - * 只匹配函数名。 - * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) - */ - public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) - { - // 为空不报错。直接返回 null - if (obj == null) - { - return null; - } - Validate.notBlank(methodName, "methodName can't be blank"); - for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) - { - Method[] methods = searchType.getDeclaredMethods(); - for (Method method : methods) - { - if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) - { - makeAccessible(method); - return method; - } - } - } - return null; - } - - /** - * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 - */ - public static void makeAccessible(Method method) - { - if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) - && !method.isAccessible()) - { - method.setAccessible(true); - } - } - - /** - * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 - */ - public static void makeAccessible(Field field) - { - if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) - || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) - { - field.setAccessible(true); - } - } - - /** - * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 - * 如无法找到, 返回Object.class. - */ - @SuppressWarnings("unchecked") - public static Class getClassGenricType(final Class clazz) - { - return getClassGenricType(clazz, 0); - } - - /** - * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. - * 如无法找到, 返回Object.class. - */ - public static Class getClassGenricType(final Class clazz, final int index) - { - Type genType = clazz.getGenericSuperclass(); - - if (!(genType instanceof ParameterizedType)) - { - logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); - return Object.class; - } - - Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); - - if (index >= params.length || index < 0) - { - logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " - + params.length); - return Object.class; - } - if (!(params[index] instanceof Class)) - { - logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); - return Object.class; - } - - return (Class) params[index]; - } - - public static Class getUserClass(Object instance) - { - if (instance == null) - { - throw new RuntimeException("Instance must not be null"); - } - Class clazz = instance.getClass(); - if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) - { - Class superClass = clazz.getSuperclass(); - if (superClass != null && !Object.class.equals(superClass)) - { - return superClass; - } - } - return clazz; - - } - - /** - * 将反射时的checked exception转换为unchecked exception. - */ - public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) - { - if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException - || e instanceof NoSuchMethodException) - { - return new IllegalArgumentException(msg, e); - } - else if (e instanceof InvocationTargetException) - { - return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); - } - return new RuntimeException(msg, e); - } -} +package com.ruoyi.common.utils.reflect; + +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; + +import java.lang.reflect.Method; +import java.util.List; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author Lion Li + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils extends ReflectUtil { + + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) { + Object object = obj; + for (String name : StrUtil.split(propertyName, ".")) { + String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(name); + object = invoke(object, getterMethodName); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) { + Object object = obj; + List names = StrUtil.split(propertyName, "."); + for (int i = 0; i < names.size(); i++) { + if (i < names.size() - 1) { + String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(names.get(i)); + object = invoke(object, getterMethodName); + } else { + String setterMethodName = SETTER_PREFIX + StrUtil.upperFirst(names.get(i)); + Method method = getMethodByName(object.getClass(), setterMethodName); + invoke(object, method, value); + } + } + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java index 8e98c504..14ff8a4d 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -1,146 +1,65 @@ package com.ruoyi.common.utils.spring; -import cn.hutool.core.lang.Validator; +import cn.hutool.extra.spring.SpringUtil; import org.springframework.aop.framework.AopContext; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** - * spring工具类 方便在非spring管理环境中获取bean - * - * @author ruoyi + * spring工具类 + * + * @author Lion Li */ @Component -public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware -{ - /** Spring应用上下文环境 */ - private static ConfigurableListableBeanFactory beanFactory; +public final class SpringUtils extends SpringUtil { - private static ApplicationContext applicationContext; + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) { + return getBeanFactory().containsBean(name); + } - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException - { - SpringUtils.beanFactory = beanFactory; - } + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 + * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().isSingleton(name); + } - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - SpringUtils.applicationContext = applicationContext; - } + /** + * @param name + * @return Class 注册对象的类型 + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getType(name); + } - /** - * 获取对象 - * - * @param name - * @return Object 一个以所给名字注册的bean的实例 - * @throws org.springframework.beans.BeansException - * - */ - @SuppressWarnings("unchecked") - public static T getBean(String name) throws BeansException - { - return (T) beanFactory.getBean(name); - } + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getAliases(name); + } - /** - * 获取类型为requiredType的对象 - * - * @param clz - * @return - * @throws org.springframework.beans.BeansException - * - */ - public static T getBean(Class clz) throws BeansException - { - T result = (T) beanFactory.getBean(clz); - return result; - } + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } - /** - * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true - * - * @param name - * @return boolean - */ - public static boolean containsBean(String name) - { - return beanFactory.containsBean(name); - } - - /** - * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) - * - * @param name - * @return boolean - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.isSingleton(name); - } - - /** - * @param name - * @return Class 注册对象的类型 - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static Class getType(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.getType(name); - } - - /** - * 如果给定的bean名字在bean定义中有别名,则返回这些别名 - * - * @param name - * @return - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static String[] getAliases(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.getAliases(name); - } - - /** - * 获取aop代理对象 - * - * @param invoker - * @return - */ - @SuppressWarnings("unchecked") - public static T getAopProxy(T invoker) - { - return (T) AopContext.currentProxy(); - } - - /** - * 获取当前的环境配置,无配置返回null - * - * @return 当前的环境配置 - */ - public static String[] getActiveProfiles() - { - return applicationContext.getEnvironment().getActiveProfiles(); - } - - /** - * 获取当前的环境配置,当有多个环境配置时,只获取第一个 - * - * @return 当前的环境配置 - */ - public static String getActiveProfile() - { - final String[] activeProfiles = getActiveProfiles(); - return Validator.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; - } } diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java index a7e79f84..10520aed 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java @@ -5,7 +5,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotBlank; - +import java.util.Date; /** @@ -40,4 +40,8 @@ public class TestDemoAddBo { @NotBlank(message = "值不能为空") private String value; + /** 创建时间 */ + @ApiModelProperty("创建时间") + private Date createTime; + } diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java index c3c71efc..f6649ed9 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java @@ -1,8 +1,10 @@ package com.ruoyi.demo.controller; -import com.ruoyi.common.annotation.RedisLock; +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; +import com.baomidou.lock.annotation.Lock4j; +import com.baomidou.lock.executor.RedissonLockExecutor; import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.redis.RedisLockManager; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; @@ -10,7 +12,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.concurrent.TimeUnit; +import java.time.LocalTime; /** @@ -24,44 +26,47 @@ import java.util.concurrent.TimeUnit; public class RedisLockController { @Autowired - private RedisLockManager redisLockManager; + private LockTemplate lockTemplate; /** - * #p0 标识取第一个参数为redis锁的key + * 测试lock4j 注解 */ - @GetMapping("/testLock1") - @RedisLock(expireTime = 10, key = "#p0") - public AjaxResult testLock1(String key, String value) { + @Lock4j(keys = {"#key"}) + @GetMapping("/testLock4j") + public AjaxResult testLock4j(String key,String value){ + System.out.println("start:"+key+",time:"+ LocalTime.now().toString()); try { - // 同时请求排队 -// Thread.sleep(5000); - // 锁超时测试 - Thread.sleep(11000); + Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } + System.out.println("end :"+key+",time:"+LocalTime.now().toString()); return AjaxResult.success("操作成功",value); } /** - * 测试锁工具类 + * 测试lock4j 工具 */ - @GetMapping("/testLock2") - public AjaxResult testLock(String key, Long time) { - try { - boolean flag = redisLockManager.getLock(key, time, TimeUnit.SECONDS); - if (flag) { - log.info("获取锁成功: " + key); - Thread.sleep(3000); - redisLockManager.unLock(key); - log.info("释放锁成功: " + key); - } else { - log.error("获取锁失败: " + key); - } - } catch (InterruptedException e) { - log.error(e.getMessage()); + @GetMapping("/testLock4jLockTemaplate") + public AjaxResult testLock4jLockTemaplate(String key,String value){ + final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class); + if (null == lockInfo) { + throw new RuntimeException("业务处理中,请稍后再试"); } - return AjaxResult.success(); + // 获取锁成功,处理业务 + try { + try { + Thread.sleep(8000); + } catch (InterruptedException e) { + // + } + System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName()); + } finally { + //释放锁 + lockTemplate.releaseLock(lockInfo); + } + //结束 + return AjaxResult.success("操作成功",value); } /** diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java new file mode 100644 index 00000000..499a5ad0 --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java @@ -0,0 +1,52 @@ +package com.ruoyi.demo.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.demo.domain.TestDemo; +import com.ruoyi.demo.service.ITestDemoService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +/** + * 测试批量方法 + * + * @author Lion Li + * @date 2021-05-30 + */ +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RestController +@RequestMapping("/demo/batch") +public class TestBatchController extends BaseController { + + private final ITestDemoService iTestDemoService; + + /** + * 新增批量方法 + */ + @PostMapping() + public AjaxResult add() { + List list = new ArrayList<>(); + for (int i = 0; i < 1000; i++) { + list.add(new TestDemo().setOrderNum(-1L).setTestKey("批量新增").setValue("测试新增")); + } + return toAjax(iTestDemoService.saveAll(list) ? 1 : 0); + } + + /** + * 修改批量方法 + */ + @DeleteMapping() + public AjaxResult edit() { + return toAjax(iTestDemoService.remove(new LambdaQueryWrapper() + .eq(TestDemo::getOrderNum, -1L)) ? 1 : 0); + } + +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java index f0387069..26f652a9 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java @@ -1,6 +1,8 @@ package com.ruoyi.demo.domain; import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @@ -23,8 +25,11 @@ public class TestDemo implements Serializable { private static final long serialVersionUID=1L; - /** 主键 */ + /** + * 主键 + */ @TableId(value = "id") + @JsonSerialize(using = ToStringSerializer.class) private Long id; /** 部门id */ @@ -64,7 +69,6 @@ public class TestDemo implements Serializable { private String updateBy; /** 删除标志 */ - @TableLogic private Long delFlag; } diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java index 43d1efe5..b9bd2ba6 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java @@ -1,13 +1,14 @@ package com.ruoyi.demo.domain; import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; + import java.io.Serializable; import java.util.Date; -import java.math.BigDecimal; -import com.ruoyi.common.annotation.Excel; /** * 测试树表对象 test_tree @@ -26,6 +27,7 @@ public class TestTree implements Serializable { /** 主键 */ @TableId(value = "id") + @JsonSerialize(using = ToStringSerializer.class) private Long id; /** 父id */ diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/feign/FeignTestService.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/feign/FeignTestService.java index c20973a7..5273a69a 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/feign/FeignTestService.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/feign/FeignTestService.java @@ -1,5 +1,6 @@ package com.ruoyi.demo.feign; +import com.ruoyi.demo.feign.constant.FeignTestConstant; import com.ruoyi.demo.feign.fallback.FeignTestFallback; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @@ -7,10 +8,15 @@ import org.springframework.web.bind.annotation.RequestParam; /** * feign测试service - * + * 规范接口 Service 无感调用 + * 常量管理请求路径 更加规范 + * 自定义容错处理 安全可靠 * @author Lion Li */ -@FeignClient(name = "baidu",url = "http://www.baidu.com",fallback = FeignTestFallback.class) +@FeignClient( + name = FeignTestConstant.BAIDU_NAME, + url = FeignTestConstant.BAIDU_URL, + fallback = FeignTestFallback.class) public interface FeignTestService { @GetMapping("/s") diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/feign/constant/FeignTestConstant.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/feign/constant/FeignTestConstant.java new file mode 100644 index 00000000..7a5b3fb8 --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/feign/constant/FeignTestConstant.java @@ -0,0 +1,9 @@ +package com.ruoyi.demo.feign.constant; + +public class FeignTestConstant { + + public static final String BAIDU_NAME = "baidu"; + + public static final String BAIDU_URL = "http://www.baidu.com"; + +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java index 5cfb9f69..aceaf6d8 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java @@ -1,7 +1,7 @@ package com.ruoyi.demo.mapper; -import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache; -import com.ruoyi.common.core.page.BaseMapperPlus; +import com.ruoyi.common.core.mybatisplus.cache.MybatisPlusRedisCache; +import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus; import com.ruoyi.demo.domain.TestDemo; import org.apache.ibatis.annotations.CacheNamespace; @@ -11,6 +11,7 @@ import org.apache.ibatis.annotations.CacheNamespace; * @author Lion Li * @date 2021-05-30 */ +// 如使需切换数据源 请勿使用缓存 会造成数据不一致现象 @CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class) public interface TestDemoMapper extends BaseMapperPlus { diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java index 0635ebc0..20a3e9be 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java @@ -1,9 +1,7 @@ package com.ruoyi.demo.mapper; -import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache; -import com.ruoyi.common.core.page.BaseMapperPlus; +import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus; import com.ruoyi.demo.domain.TestTree; -import org.apache.ibatis.annotations.CacheNamespace; /** * 测试树表Mapper接口 @@ -11,7 +9,7 @@ import org.apache.ibatis.annotations.CacheNamespace; * @author Lion Li * @date 2021-05-30 */ -@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class) +//@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class) public interface TestTreeMapper extends BaseMapperPlus { } diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java index 7f08668f..c011325a 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java @@ -5,7 +5,7 @@ import com.ruoyi.demo.vo.TestDemoVo; import com.ruoyi.demo.bo.TestDemoQueryBo; import com.ruoyi.demo.bo.TestDemoAddBo; import com.ruoyi.demo.bo.TestDemoEditBo; -import com.ruoyi.common.core.page.IServicePlus; +import com.ruoyi.common.core.mybatisplus.core.IServicePlus; import com.ruoyi.common.core.page.TableDataInfo; import java.util.Collection; @@ -18,6 +18,7 @@ import java.util.List; * @date 2021-05-30 */ public interface ITestDemoService extends IServicePlus { + /** * 查询单个 * @return diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java index 605b3dfd..6f7be37c 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java @@ -1,6 +1,6 @@ package com.ruoyi.demo.service; -import com.ruoyi.common.core.page.IServicePlus; +import com.ruoyi.common.core.mybatisplus.core.IServicePlus; import com.ruoyi.demo.bo.TestTreeAddBo; import com.ruoyi.demo.bo.TestTreeEditBo; import com.ruoyi.demo.bo.TestTreeQueryBo; diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java index d974762f..4d35efe0 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java @@ -4,8 +4,8 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.common.core.page.PagePlus; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.utils.PageUtils; @@ -29,7 +29,7 @@ import java.util.Map; * @date 2021-05-30 */ @Service -public class TestDemoServiceImpl extends ServiceImpl implements ITestDemoService { +public class TestDemoServiceImpl extends ServicePlusImpl implements ITestDemoService { @Override public TestDemoVo queryById(Long id) { diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java index ae93ef37..36482f19 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java @@ -4,8 +4,8 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.demo.bo.TestTreeAddBo; import com.ruoyi.demo.bo.TestTreeEditBo; import com.ruoyi.demo.bo.TestTreeQueryBo; @@ -26,13 +26,14 @@ import java.util.Map; * @date 2021-05-30 */ @Service -public class TestTreeServiceImpl extends ServiceImpl implements ITestTreeService { +public class TestTreeServiceImpl extends ServicePlusImpl implements ITestTreeService { @Override public TestTreeVo queryById(Long id) { return getVoById(id, TestTreeVo.class); } +// @DataSource(DataSourceType.SLAVE) // 切换从库查询 @DataScope(isUser = true) @Override public List queryList(TestTreeQueryBo bo) { diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestDemoVo.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestDemoVo.java index c51ed43b..d1eb8bf6 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestDemoVo.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestDemoVo.java @@ -1,10 +1,12 @@ package com.ruoyi.demo.vo; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.ruoyi.common.annotation.Excel; -import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; + import java.util.Date; @@ -21,8 +23,13 @@ public class TestDemoVo { private static final long serialVersionUID = 1L; - /** 主键 */ + /** + * 主键 + * 如果是自定义id 或者 雪花id + * 需要增加序列化为字符串注解 因为Long到前端会失真 + */ @ApiModelProperty("主键") + @JsonSerialize(using = ToStringSerializer.class) private Long id; /** 部门id */ @@ -52,7 +59,7 @@ public class TestDemoVo { /** 创建时间 */ @Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ApiModelProperty("创建时间") private Date createTime; @@ -63,7 +70,7 @@ public class TestDemoVo { /** 更新时间 */ @Excel(name = "更新时间" , width = 30, dateFormat = "yyyy-MM-dd") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ApiModelProperty("更新时间") private Date updateTime; diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestTreeVo.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestTreeVo.java index 44413b12..c6ea5587 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestTreeVo.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestTreeVo.java @@ -1,10 +1,13 @@ package com.ruoyi.demo.vo; -import com.ruoyi.common.annotation.Excel; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.ruoyi.common.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; + import java.util.Date; @@ -23,6 +26,7 @@ public class TestTreeVo { /** 主键 */ @ApiModelProperty("主键") + @JsonSerialize(using = ToStringSerializer.class) private Long id; /** 父id */ diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java index f92a9647..f82b2ae4 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -1,15 +1,13 @@ package com.ruoyi.framework.aspectj; import cn.hutool.core.lang.Validator; +import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; import com.ruoyi.common.annotation.DataSource; -import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @@ -18,56 +16,47 @@ import java.util.Objects; /** * 多数据源处理 - * + * * @author ruoyi */ @Aspect -@Order(1) +@Order(-500) @Component -public class DataSourceAspect -{ - protected Logger logger = LoggerFactory.getLogger(getClass()); +public class DataSourceAspect { - @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" - + "|| @within(com.ruoyi.common.annotation.DataSource)") - public void dsPointCut() - { + @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" + + "|| @within(com.ruoyi.common.annotation.DataSource)") + public void dsPointCut() { + } - } + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + DataSource dataSource = getDataSource(point); - @Around("dsPointCut()") - public Object around(ProceedingJoinPoint point) throws Throwable - { - DataSource dataSource = getDataSource(point); + if (Validator.isNotNull(dataSource)) { + DynamicDataSourceContextHolder.poll(); + String source = dataSource.value().getSource(); + DynamicDataSourceContextHolder.push(source); + } - if (Validator.isNotNull(dataSource)) - { - DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); - } + try { + return point.proceed(); + } finally { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clear(); + } + } - try - { - return point.proceed(); - } - finally - { - // 销毁数据源 在执行方法之后 - DynamicDataSourceContextHolder.clearDataSourceType(); - } - } + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) { + MethodSignature signature = (MethodSignature) point.getSignature(); + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); + if (Objects.nonNull(dataSource)) { + return dataSource; + } - /** - * 获取需要切换的数据源 - */ - public DataSource getDataSource(ProceedingJoinPoint point) - { - MethodSignature signature = (MethodSignature) point.getSignature(); - DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); - if (Objects.nonNull(dataSource)) - { - return dataSource; - } - - return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); - } + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java index 442d2589..d5c0fe8d 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -2,16 +2,14 @@ package com.ruoyi.framework.aspectj; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSON; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.enums.BusinessStatus; import com.ruoyi.common.enums.HttpMethod; +import com.ruoyi.common.utils.JsonUtils; import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.manager.AsyncManager; -import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.AsyncService; import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.domain.SysOperLog; import org.aspectj.lang.JoinPoint; @@ -32,7 +30,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; import java.util.Collection; -import java.util.Iterator; import java.util.Map; /** @@ -93,10 +90,10 @@ public class LogAspect SysOperLog operLog = new SysOperLog(); operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); // 请求的地址 - String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + String ip = ServletUtils.getClientIP(); operLog.setOperIp(ip); // 返回参数 - operLog.setJsonResult(JSON.toJSONString(jsonResult)); + operLog.setJsonResult(JsonUtils.toJsonString(jsonResult)); operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); if (loginUser != null) @@ -118,7 +115,7 @@ public class LogAspect // 处理设置注解上的参数 getControllerMethodDescription(joinPoint, controllerLog, operLog); // 保存数据库 - AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + SpringUtils.getBean(AsyncService.class).recordOper(operLog); } catch (Exception exp) { @@ -194,19 +191,16 @@ public class LogAspect */ private String argsArrayToString(Object[] paramsArray) { - String params = ""; + StringBuilder params = new StringBuilder(); if (paramsArray != null && paramsArray.length > 0) { - for (int i = 0; i < paramsArray.length; i++) - { - if (Validator.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i])) - { - Object jsonObj = JSON.toJSON(paramsArray[i]); - params += jsonObj.toString() + " "; - } - } + for (Object o : paramsArray) { + if (Validator.isNotNull(o) && !isFilterObject(o)) { + params.append(JsonUtils.toJsonString(o)).append(" "); + } + } } - return params.trim(); + return params.toString().trim(); } /** @@ -226,19 +220,17 @@ public class LogAspect else if (Collection.class.isAssignableFrom(clazz)) { Collection collection = (Collection) o; - for (Iterator iter = collection.iterator(); iter.hasNext();) - { - return iter.next() instanceof MultipartFile; - } + for (Object value : collection) { + return value instanceof MultipartFile; + } } else if (Map.class.isAssignableFrom(clazz)) { Map map = (Map) o; - for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) - { - Map.Entry entry = (Map.Entry) iter.next(); - return entry.getValue() instanceof MultipartFile; - } + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } } return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof BindingResult; diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RedisLockAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RedisLockAspect.java deleted file mode 100644 index d81c3ca8..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RedisLockAspect.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.ruoyi.framework.aspectj; - - -import com.ruoyi.common.annotation.RedisLock; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.redis.RedisLockManager; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; -import org.redisson.api.RLock; -import org.redisson.api.RedissonClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -/** - * 分布式锁(注解实现版本) - * - * @author shenxinquan - */ - -@Slf4j -@Aspect -@Order(9) -@Component -public class RedisLockAspect { - - @Autowired - private RedisLockManager redisLockManager; - - @Pointcut("@annotation(com.ruoyi.common.annotation.RedisLock)") - public void annotationPointcut() { - } - - @Around("annotationPointcut()") - public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { - // 获得当前访问的class - Class className = joinPoint.getTarget().getClass(); - // 获得访问的方法名 - String methodName = joinPoint.getSignature().getName(); - // 得到方法的参数的类型 - Class[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes(); - Object[] args = joinPoint.getArgs(); - String key = ""; - // 默认30秒过期时间 - int expireTime = 30; - - try { - // 得到访问的方法对象 - Method method = className.getMethod(methodName, argClass); - method.setAccessible(true); - // 判断是否存在@RedisLock注解 - if (method.isAnnotationPresent(RedisLock.class)) { - RedisLock annotation = method.getAnnotation(RedisLock.class); - key = getRedisKey(args, annotation.key()); - expireTime = getExpireTime(annotation); - } - } catch (Exception e) { - throw new RuntimeException("redis分布式锁注解参数异常", e); - } - - // 声明锁名称 - key = Constants.REDIS_LOCK_KEY + key; - Object res; - try { - if (redisLockManager.getLock(key, expireTime, TimeUnit.SECONDS)) { - log.info("lock => key : " + key + " , ThreadName : " + Thread.currentThread().getName()); - try { - res = joinPoint.proceed(); - return res; - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - redisLockManager.unLock(key); - log.info("unlock => key : " + key + " , ThreadName : " + Thread.currentThread().getName()); - } - } else { - throw new RuntimeException("redis分布式锁注解参数异常"); - } - } catch (IllegalMonitorStateException e) { - log.error("lock timeout => key : " + key + " , ThreadName : " + Thread.currentThread().getName()); - throw new RuntimeException("lock timeout => key : " + key); - } catch (Exception e) { - throw new Exception("redis分布式未知异常", e); - } - } - - private int getExpireTime(RedisLock annotation) { - return annotation.expireTime(); - } - - private String getRedisKey(Object[] args, String primalKey) { - if (args.length == 0) { - return primalKey; - } - // 获取#p0...集合 - List keyList = getKeyParsList(primalKey); - for (String keyName : keyList) { - int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", "")); - Object parValue = args[keyIndex]; - primalKey = primalKey.replace(keyName, String.valueOf(parValue)); - } - return primalKey.replace("+", "").replace("'", ""); - } - - /** - * 获取key中#p0中的参数名称 - */ - private static List getKeyParsList(String key) { - List listPar = new ArrayList<>(); - if (key.contains("#")) { - int plusIndex = key.substring(key.indexOf("#")).indexOf("+"); - int indexNext = 0; - String parName; - int indexPre = key.indexOf("#"); - if (plusIndex > 0) { - indexNext = key.indexOf("#") + plusIndex; - parName = key.substring(indexPre, indexNext); - } else { - parName = key.substring(indexPre); - } - listPar.add(parName.trim()); - key = key.substring(indexNext + 1); - if (key.contains("#")) { - listPar.addAll(getKeyParsList(key)); - } - } - return listPar; - } - -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java index 5c11ae67..2eb4eb3f 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java @@ -2,6 +2,8 @@ package com.ruoyi.framework.config; import com.ruoyi.common.exception.CustomException; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurerSupport; import org.springframework.scheduling.annotation.EnableAsync; @@ -9,7 +11,7 @@ import org.springframework.security.concurrent.DelegatingSecurityContextExecutor import java.util.Arrays; import java.util.concurrent.Executor; -import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; /** * 异步配置 @@ -20,13 +22,16 @@ import java.util.concurrent.Executors; @Configuration public class AsyncConfig extends AsyncConfigurerSupport { + @Autowired + @Qualifier("scheduledExecutorService") + private ScheduledExecutorService scheduledExecutorService; + /** * 异步执行需要使用权限框架自带的包装线程池 保证权限信息的传递 */ @Override public Executor getAsyncExecutor() { - return new DelegatingSecurityContextExecutorService( - Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())); + return new DelegatingSecurityContextExecutorService(scheduledExecutorService); } /** diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java index 3fab843f..735f7e35 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -1,82 +1,22 @@ package com.ruoyi.framework.config; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.sql.DataSource; +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; -import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; -import com.alibaba.druid.util.Utils; -import com.ruoyi.common.enums.DataSourceType; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.config.properties.DruidProperties; -import com.ruoyi.framework.datasource.DynamicDataSource; + +import javax.servlet.*; +import java.io.IOException; /** * druid 配置多数据源 - * + * * @author ruoyi */ @Configuration -public class DruidConfig -{ - @Bean - @ConfigurationProperties("spring.datasource.druid.master") - public DataSource masterDataSource(DruidProperties druidProperties) - { - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); - return druidProperties.dataSource(dataSource); - } - - @Bean - @ConfigurationProperties("spring.datasource.druid.slave") - @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") - public DataSource slaveDataSource(DruidProperties druidProperties) - { - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); - return druidProperties.dataSource(dataSource); - } - - @Bean(name = "dynamicDataSource") - @Primary - public DynamicDataSource dataSource(DataSource masterDataSource) - { - Map targetDataSources = new HashMap<>(); - targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); - setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); - return new DynamicDataSource(masterDataSource, targetDataSources); - } - - /** - * 设置数据源 - * - * @param targetDataSources 备选数据源集合 - * @param sourceName 数据源名称 - * @param beanName bean名称 - */ - public void setDataSource(Map targetDataSources, String sourceName, String beanName) - { - try - { - DataSource dataSource = SpringUtils.getBean(beanName); - targetDataSources.put(sourceName, dataSource); - } - catch (Exception e) - { - } - } +public class DruidConfig { /** * 去除监控页面底部的广告 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FeignConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FeignConfig.java index 478a450f..f10769bc 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FeignConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FeignConfig.java @@ -1,7 +1,6 @@ package com.ruoyi.framework.config; import feign.*; -import feign.hystrix.HystrixFeign; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; import org.springframework.boot.autoconfigure.AutoConfigureBefore; @@ -35,11 +34,6 @@ public class FeignConfig { .build(); } - @Bean - public Feign.Builder feignBuilder() { - return HystrixFeign.builder(); - } - @Bean public Contract feignContract() { return new SpringMvcContract(); @@ -60,4 +54,4 @@ public class FeignConfig { return new Retryer.Default(); } -} \ No newline at end of file +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java index 73f7f936..b09bc98a 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java @@ -30,7 +30,7 @@ public class FilterConfig { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setDispatcherTypes(DispatcherType.REQUEST); registration.setFilter(new XssFilter()); - registration.addUrlPatterns(StrUtil.split(xssProperties.getUrlPatterns(), ",")); + registration.addUrlPatterns(StrUtil.splitToArray(xssProperties.getUrlPatterns(), ",")); registration.setName("xssFilter"); registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); Map initParameters = new HashMap(); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java index b367100e..8334b2a9 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java @@ -2,35 +2,47 @@ package com.ruoyi.framework.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.ruoyi.common.utils.JsonUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; + +import java.time.LocalDateTime; +import java.util.TimeZone; /** - * 当Mybatis plus设置为雪花ID时 - * 使用此类,会把所有数字返回变为字符串返回适配前端Long型失真问题 + * jackson 配置 * - * @author Ming LI + * @author Lion Li */ +@Slf4j @Configuration public class JacksonConfig { @Bean - @Primary - @ConditionalOnMissingBean(ObjectMapper.class) - @ConditionalOnProperty(value = "mybatis-plus.global-config.dbConfig.idType", havingValue = "ASSIGN_ID") - public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { - ObjectMapper objectMapper = builder.createXmlMapper(false).build(); - // 全局配置序列化返回 JSON 处理 - SimpleModule simpleModule = new SimpleModule(); - //JSON Long ==> String - simpleModule.addSerializer(Long.class, ToStringSerializer.instance); - objectMapper.registerModule(simpleModule); - return objectMapper; + public BeanPostProcessor objectMapperBeanPostProcessor() { + return new BeanPostProcessor() { + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof ObjectMapper)) { + return bean; + } + ObjectMapper objectMapper = (ObjectMapper) bean; + // 全局配置序列化返回 JSON 处理 + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE); + simpleModule.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE); + objectMapper.registerModule(simpleModule); + objectMapper.setTimeZone(TimeZone.getDefault()); + JsonUtils.init(objectMapper); + log.info("初始化 jackson 配置"); + return bean; + } + }; } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java index bce2150b..1246a295 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java @@ -2,15 +2,21 @@ package com.ruoyi.framework.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; +import com.baomidou.mybatisplus.core.injector.ISqlInjector; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.ruoyi.common.core.mybatisplus.methods.InsertAll; import com.ruoyi.framework.mybatisplus.CreateAndUpdateMetaObjectHandler; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; +import java.util.List; + /** * mybatis-plus配置类 * @@ -94,10 +100,17 @@ public class MybatisPlusConfig { * sql注入器配置 * https://baomidou.com/guide/sql-injector.html */ -// @Bean -// public ISqlInjector sqlInjector() { -// return new DefaultSqlInjector(); -// } + @Bean + public ISqlInjector sqlInjector() { + return new DefaultSqlInjector() { + @Override + public List getMethodList(Class mapperClass) { + List methodList = super.getMethodList(mapperClass); + methodList.add(new InsertAll()); + return methodList; + } + }; + } /** * TenantLineInnerInterceptor 多租户插件 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java index 005f0323..c3cdc943 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -29,9 +29,6 @@ public class ResourcesConfig implements WebMvcConfigurer { /** 本地文件上传路径 */ registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); - - /** swagger配置 */ - registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/"); } /** @@ -53,7 +50,7 @@ public class ResourcesConfig implements WebMvcConfigurer CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); // 设置访问源地址 - config.addAllowedOrigin("*"); + config.addAllowedOriginPattern("*"); // 设置访问源请求头 config.addAllowedHeader("*"); // 设置访问源请求方法 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index 4822c912..79ade787 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -21,7 +21,7 @@ import org.springframework.web.filter.CorsFilter; /** * spring security配置 - * + * * @author ruoyi */ @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) @@ -32,7 +32,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter */ @Autowired private UserDetailsService userDetailsService; - + /** * 认证失败处理类 */ @@ -59,7 +59,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter @Autowired private AdminServerProperties adminServerProperties; - + /** * 解决 无法直接注入 AuthenticationManager * @@ -112,7 +112,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter .antMatchers("/profile/**").anonymous() .antMatchers("/common/download**").anonymous() .antMatchers("/common/download/resource**").anonymous() - .antMatchers("/swagger-ui.html").anonymous() + .antMatchers("/doc.html").anonymous() .antMatchers("/swagger-resources/**").anonymous() .antMatchers("/webjars/**").anonymous() .antMatchers("/*/api-docs").anonymous() @@ -135,7 +135,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); } - + /** * 强散列哈希加密实现 */ diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java deleted file mode 100644 index 03ddc2c5..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.ruoyi.framework.config.properties; - -import com.alibaba.druid.pool.DruidDataSource; -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -/** - * druid 配置属性 - * - * @author Lion Li - */ -@Data -@Configuration -@ConfigurationProperties(prefix = "spring.datasource.druid") -public class DruidProperties { - - /** 初始连接数 */ - private int initialSize; - /** 最小连接池数量 */ - private int minIdle; - /** 最大连接池数量 */ - private int maxActive; - /** 配置获取连接等待超时的时间 */ - private int maxWait; - /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ - private int timeBetweenEvictionRunsMillis; - /** 配置一个连接在池中最小生存的时间,单位是毫秒 */ - private int minEvictableIdleTimeMillis; - /** 配置一个连接在池中最大生存的时间,单位是毫秒 */ - private int maxEvictableIdleTimeMillis; - /** 配置检测连接是否有效 */ - private String validationQuery; - /** 初始连接数 */ - private boolean testWhileIdle; - /** 初始连接数 */ - private boolean testOnBorrow; - /** 初始连接数 */ - private boolean testOnReturn; - - public DruidDataSource dataSource(DruidDataSource datasource) { - datasource.setInitialSize(initialSize); - datasource.setMaxActive(maxActive); - datasource.setMinIdle(minIdle); - datasource.setMaxWait(maxWait); - datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); - datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); - datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); - datasource.setValidationQuery(validationQuery); - datasource.setTestWhileIdle(testWhileIdle); - datasource.setTestOnBorrow(testOnBorrow); - datasource.setTestOnReturn(testOnReturn); - return datasource; - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java deleted file mode 100644 index e70b8cfa..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.ruoyi.framework.datasource; - -import java.util.Map; -import javax.sql.DataSource; -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; - -/** - * 动态数据源 - * - * @author ruoyi - */ -public class DynamicDataSource extends AbstractRoutingDataSource -{ - public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) - { - super.setDefaultTargetDataSource(defaultTargetDataSource); - super.setTargetDataSources(targetDataSources); - super.afterPropertiesSet(); - } - - @Override - protected Object determineCurrentLookupKey() - { - return DynamicDataSourceContextHolder.getDataSourceType(); - } -} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java deleted file mode 100644 index 3572db91..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.ruoyi.framework.datasource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 数据源切换处理 - * - * @author ruoyi - */ -public class DynamicDataSourceContextHolder -{ - public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); - - /** - * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, - * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 - */ - private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); - - /** - * 设置数据源的变量 - */ - public static void setDataSourceType(String dsType) - { - log.info("切换到{}数据源", dsType); - CONTEXT_HOLDER.set(dsType); - } - - /** - * 获得数据源的变量 - */ - public static String getDataSourceType() - { - return CONTEXT_HOLDER.get(); - } - - /** - * 清空数据源变量 - */ - public static void clearDataSourceType() - { - CONTEXT_HOLDER.remove(); - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java index d310382f..bd0e99ed 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java @@ -1,55 +1,56 @@ -package com.ruoyi.framework.interceptor; - -import java.lang.reflect.Method; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.springframework.stereotype.Component; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; -import com.alibaba.fastjson.JSONObject; -import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.utils.ServletUtils; - -/** - * 防止重复提交拦截器 - * - * @author ruoyi - */ -@Component -public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter -{ - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception - { - if (handler instanceof HandlerMethod) - { - HandlerMethod handlerMethod = (HandlerMethod) handler; - Method method = handlerMethod.getMethod(); - RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); - if (annotation != null) - { - if (this.isRepeatSubmit(request)) - { - AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试"); - ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult)); - return false; - } - } - return true; - } - else - { - return super.preHandle(request, response, handler); - } - } - - /** - * 验证是否重复提交由子类实现具体的防重复提交的规则 - * - * @param request - * @return - * @throws Exception - */ - public abstract boolean isRepeatSubmit(HttpServletRequest request); -} +package com.ruoyi.framework.interceptor; + +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.JsonUtils; +import com.ruoyi.common.utils.ServletUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; + +/** + * 防止重复提交拦截器 + * + * @author ruoyi + */ +@Component +public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter +{ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception + { + if (handler instanceof HandlerMethod) + { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) + { + if (this.isRepeatSubmit(request)) + { + AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试"); + ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return super.preHandle(request, response, handler); + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request + * @return + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request); +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java index 60bbf7ca..bc092314 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -1,126 +1,133 @@ -package com.ruoyi.framework.interceptor.impl; - -import cn.hutool.core.lang.Validator; -import com.alibaba.fastjson.JSONObject; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.filter.RepeatedlyRequestWrapper; -import com.ruoyi.common.utils.http.HttpHelper; -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * 判断请求url和数据是否和上一次相同, - * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 - * - * @author ruoyi - */ -@Component -public class SameUrlDataInterceptor extends RepeatSubmitInterceptor -{ - public final String REPEAT_PARAMS = "repeatParams"; - - public final String REPEAT_TIME = "repeatTime"; - - // 令牌自定义标识 - @Value("${token.header}") - private String header; - - @Autowired - private RedisCache redisCache; - - /** - * 间隔时间,单位:秒 默认10秒 - * - * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据 - */ - private int intervalTime = 10; - - public void setIntervalTime(int intervalTime) - { - this.intervalTime = intervalTime; - } - - @SuppressWarnings("unchecked") - @Override - public boolean isRepeatSubmit(HttpServletRequest request) - { - String nowParams = ""; - if (request instanceof RepeatedlyRequestWrapper) - { - RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; - nowParams = HttpHelper.getBodyString(repeatedlyRequest); - } - - // body参数为空,获取Parameter的数据 - if (Validator.isEmpty(nowParams)) - { - nowParams = JSONObject.toJSONString(request.getParameterMap()); - } - Map nowDataMap = new HashMap(); - nowDataMap.put(REPEAT_PARAMS, nowParams); - nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); - - // 请求地址(作为存放cache的key值) - String url = request.getRequestURI(); - - // 唯一值(没有消息头则使用请求地址) - String submitKey = request.getHeader(header); - if (Validator.isEmpty(submitKey)) - { - submitKey = url; - } - - // 唯一标识(指定key + 消息头) - String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey; - - Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); - if (sessionObj != null) - { - Map sessionMap = (Map) sessionObj; - if (sessionMap.containsKey(url)) - { - Map preDataMap = (Map) sessionMap.get(url); - if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap)) - { - return true; - } - } - } - Map cacheMap = new HashMap(); - cacheMap.put(url, nowDataMap); - redisCache.setCacheObject(cacheRepeatKey, cacheMap, intervalTime, TimeUnit.SECONDS); - return false; - } - - /** - * 判断参数是否相同 - */ - private boolean compareParams(Map nowMap, Map preMap) - { - String nowParams = (String) nowMap.get(REPEAT_PARAMS); - String preParams = (String) preMap.get(REPEAT_PARAMS); - return nowParams.equals(preParams); - } - - /** - * 判断两次间隔时间 - */ - private boolean compareTime(Map nowMap, Map preMap) - { - long time1 = (Long) nowMap.get(REPEAT_TIME); - long time2 = (Long) preMap.get(REPEAT_TIME); - if ((time1 - time2) < (this.intervalTime * 1000)) - { - return true; - } - return false; - } -} +package com.ruoyi.framework.interceptor.impl; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.Validator; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.filter.RepeatedlyRequestWrapper; +import com.ruoyi.common.utils.JsonUtils; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author ruoyi + */ +@Slf4j +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + @Autowired + private RedisCache redisCache; + + /** + * 间隔时间,单位:秒 默认10秒 + * + * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据 + */ + private int intervalTime = 10; + + public void setIntervalTime(int intervalTime) + { + this.intervalTime = intervalTime; + } + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + try { + nowParams = IoUtil.readUtf8(repeatedlyRequest.getInputStream()); + } catch (IOException e) { + log.warn("读取流出现问题!"); + } + } + + // body参数为空,获取Parameter的数据 + if (Validator.isEmpty(nowParams)) + { + nowParams = JsonUtils.toJsonString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = request.getHeader(header); + if (Validator.isEmpty(submitKey)) + { + submitKey = url; + } + + // 唯一标识(指定key + 消息头) + String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey; + + Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap)) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(cacheRepeatKey, cacheMap, intervalTime, TimeUnit.SECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < (this.intervalTime * 1000)) + { + return true; + } + return false; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java deleted file mode 100644 index 7387a02c..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.ruoyi.framework.manager; - -import java.util.TimerTask; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import com.ruoyi.common.utils.Threads; -import com.ruoyi.common.utils.spring.SpringUtils; - -/** - * 异步任务管理器 - * - * @author ruoyi - */ -public class AsyncManager -{ - /** - * 操作延迟10毫秒 - */ - private final int OPERATE_DELAY_TIME = 10; - - /** - * 异步操作任务调度线程池 - */ - private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); - - /** - * 单例模式 - */ - private AsyncManager(){} - - private static AsyncManager me = new AsyncManager(); - - public static AsyncManager me() - { - return me; - } - - /** - * 执行任务 - * - * @param task 任务 - */ - public void execute(TimerTask task) - { - executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); - } - - /** - * 停止任务线程池 - */ - public void shutdown() - { - Threads.shutdownAndAwaitTermination(executor); - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java index e36ca3c5..4ed53669 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -1,39 +1,41 @@ package com.ruoyi.framework.manager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.ruoyi.common.utils.Threads; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; + import javax.annotation.PreDestroy; +import java.util.concurrent.ScheduledExecutorService; /** * 确保应用退出时能关闭后台线程 * - * @author ruoyi + * @author Lion Li */ +@Slf4j(topic = "sys-user") @Component -public class ShutdownManager -{ - private static final Logger logger = LoggerFactory.getLogger("sys-user"); +public class ShutdownManager { - @PreDestroy - public void destroy() - { - shutdownAsyncManager(); - } + @Autowired + @Qualifier("scheduledExecutorService") + private ScheduledExecutorService scheduledExecutorService; - /** - * 停止异步执行任务 - */ - private void shutdownAsyncManager() - { - try - { - logger.info("====关闭后台任务任务线程池===="); - AsyncManager.me().shutdown(); - } - catch (Exception e) - { - logger.error(e.getMessage(), e); - } - } + @PreDestroy + public void destroy() { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() { + try { + log.info("====关闭后台任务任务线程池===="); + Threads.shutdownAndAwaitTermination(scheduledExecutorService); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java deleted file mode 100644 index d0147a2f..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.ruoyi.framework.manager.factory; - -import cn.hutool.http.useragent.UserAgent; -import cn.hutool.http.useragent.UserAgentUtil; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.ip.AddressUtils; -import com.ruoyi.common.utils.ip.IpUtils; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.system.domain.SysLogininfor; -import com.ruoyi.system.domain.SysOperLog; -import com.ruoyi.system.service.ISysLogininforService; -import com.ruoyi.system.service.ISysOperLogService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.TimerTask; - -/** - * 异步工厂(产生任务用) - * - * @author ruoyi - */ -public class AsyncFactory -{ - private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); - - /** - * 记录登录信息 - * - * @param username 用户名 - * @param status 状态 - * @param message 消息 - * @param args 列表 - * @return 任务task - */ - public static TimerTask recordLogininfor(final String username, final String status, final String message, - final Object... args) - { - final UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); - final String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); - return new TimerTask() - { - @Override - public void run() - { - String address = AddressUtils.getRealAddressByIP(ip); - StringBuilder s = new StringBuilder(); - s.append(getBlock(ip)); - s.append(address); - s.append(getBlock(username)); - s.append(getBlock(status)); - s.append(getBlock(message)); - // 打印信息到日志 - sys_user_logger.info(s.toString(), args); - // 获取客户端操作系统 - String os = userAgent.getOs().getName(); - // 获取客户端浏览器 - String browser = userAgent.getBrowser().getName(); - // 封装对象 - SysLogininfor logininfor = new SysLogininfor(); - logininfor.setUserName(username); - logininfor.setIpaddr(ip); - logininfor.setLoginLocation(address); - logininfor.setBrowser(browser); - logininfor.setOs(os); - logininfor.setMsg(message); - // 日志状态 - if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) - { - logininfor.setStatus(Constants.SUCCESS); - } - else if (Constants.LOGIN_FAIL.equals(status)) - { - logininfor.setStatus(Constants.FAIL); - } - // 插入数据 - SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); - } - }; - } - - /** - * 操作日志记录 - * - * @param operLog 操作日志信息 - * @return 任务task - */ - public static TimerTask recordOper(final SysOperLog operLog) - { - return new TimerTask() - { - @Override - public void run() - { - // 远程查询操作地点 - operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); - SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); - } - }; - } - - public static String getBlock(Object msg) - { - if (msg == null) - { - msg = ""; - } - return "[" + msg.toString() + "]"; - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java index 0ff60e24..1cd8d604 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java @@ -2,8 +2,8 @@ package com.ruoyi.framework.security.handle; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpStatus; -import com.alibaba.fastjson.JSON; import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.JsonUtils; import com.ruoyi.common.utils.ServletUtils; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; @@ -16,7 +16,7 @@ import java.io.Serializable; /** * 认证失败处理类 返回未授权 - * + * * @author ruoyi */ @Component @@ -30,6 +30,6 @@ public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, S { int code = HttpStatus.HTTP_UNAUTHORIZED; String msg = StrUtil.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); - ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(code, msg))); } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java index e79f468f..2a12126a 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -2,13 +2,12 @@ package com.ruoyi.framework.security.handle; import cn.hutool.core.lang.Validator; import cn.hutool.http.HttpStatus; -import com.alibaba.fastjson.JSON; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.JsonUtils; import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.framework.manager.AsyncManager; -import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.AsyncService; import com.ruoyi.framework.web.service.TokenService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; @@ -22,33 +21,33 @@ import java.io.IOException; /** * 自定义退出处理类 返回成功 - * + * * @author ruoyi */ @Configuration -public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler -{ - @Autowired - private TokenService tokenService; +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { + + @Autowired + private TokenService tokenService; + + @Autowired + private AsyncService asyncService; + + /** + * 退出处理 + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { + LoginUser loginUser = tokenService.getLoginUser(request); + if (Validator.isNotNull(loginUser)) { + String userName = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + asyncService.recordLogininfor(userName, Constants.LOGOUT, "退出成功", request); + } + ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(HttpStatus.HTTP_OK, "退出成功"))); + } - /** - * 退出处理 - * - * @return - */ - @Override - public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) - throws IOException, ServletException - { - LoginUser loginUser = tokenService.getLoginUser(request); - if (Validator.isNotNull(loginUser)) - { - String userName = loginUser.getUsername(); - // 删除用户缓存记录 - tokenService.delLoginUser(loginUser.getToken()); - // 记录用户退出日志 - AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); - } - ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.HTTP_OK, "退出成功"))); - } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java new file mode 100644 index 00000000..e64fbc15 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java @@ -0,0 +1,96 @@ +package com.ruoyi.framework.web.service; + +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.service.ISysLogininforService; +import com.ruoyi.system.service.ISysOperLogService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; + +/** + * 异步工厂(产生任务用) + * + * @author Lion Li + */ +@Slf4j(topic = "sys-user") +@Async +@Component +public class AsyncService { + + @Autowired + private ISysLogininforService iSysLogininforService; + + @Autowired + private ISysOperLogService iSysOperLogService; + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + */ + public void recordLogininfor(final String username, final String status, final String message, + HttpServletRequest request, final Object... args) { + final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); + final String ip = ServletUtils.getClientIP(request); + + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(getBlock(ip)); + s.append(address); + s.append(getBlock(username)); + s.append(getBlock(status)); + s.append(getBlock(message)); + // 打印信息到日志 + log.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOs().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) { + logininfor.setStatus(Constants.SUCCESS); + } else if (Constants.LOGIN_FAIL.equals(status)) { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + iSysLogininforService.insertLogininfor(logininfor); + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + */ + public void recordOper(final SysOperLog operLog) { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + iSysOperLogService.insertOperlog(operLog); + } + + private String getBlock(Object msg) { + if (msg == null) { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java index bc22ca04..d0b9511e 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java @@ -11,10 +11,7 @@ import com.ruoyi.common.exception.user.UserPasswordNotMatchException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.framework.config.properties.CaptchaProperties; -import com.ruoyi.framework.manager.AsyncManager; -import com.ruoyi.framework.manager.factory.AsyncFactory; import com.ruoyi.system.service.ISysUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; @@ -24,6 +21,7 @@ import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; /** * 登录校验方法 @@ -48,6 +46,9 @@ public class SysLoginService @Autowired private ISysUserService userService; + @Autowired + private AsyncService asyncService; + /** * 登录验证 * @@ -59,16 +60,17 @@ public class SysLoginService */ public String login(String username, String password, String code, String uuid) { + HttpServletRequest request = ServletUtils.getRequest(); if(captchaProperties.getEnabled()) { String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; String captcha = redisCache.getCacheObject(verifyKey); redisCache.deleteObject(verifyKey); if (captcha == null) { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request); throw new CaptchaExpireException(); } if (!code.equalsIgnoreCase(captcha)) { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request); throw new CaptchaException(); } } @@ -84,16 +86,16 @@ public class SysLoginService { if (e instanceof BadCredentialsException) { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request); throw new UserPasswordNotMatchException(); } else { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage(), request); throw new CustomException(e.getMessage()); } } - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); LoginUser loginUser = (LoginUser) authentication.getPrincipal(); recordLoginInfo(loginUser.getUser()); // 生成token @@ -105,7 +107,7 @@ public class SysLoginService */ public void recordLoginInfo(SysUser user) { - user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest())); + user.setLoginIp(ServletUtils.getClientIP()); user.setLoginDate(DateUtils.getNowDate()); user.setUpdateBy(user.getUserName()); userService.updateUserProfile(user); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java index 0db777bc..f8345707 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java @@ -9,7 +9,6 @@ import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.ip.AddressUtils; -import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.framework.config.properties.TokenProperties; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; @@ -131,7 +130,7 @@ public class TokenService { */ public void setUserAgent(LoginUser loginUser) { UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); - String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + String ip = ServletUtils.getClientIP(); loginUser.setIpaddr(ip); loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); loginUser.setBrowser(userAgent.getBrowser().getName()); diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java index 5a2691a9..dc510091 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java @@ -1,6 +1,6 @@ package com.ruoyi.generator.mapper; -import com.ruoyi.common.core.page.BaseMapperPlus; +import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus; import com.ruoyi.generator.domain.GenTableColumn; import java.util.List; diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java index 85da309f..3d3e4b9e 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java @@ -1,7 +1,7 @@ package com.ruoyi.generator.mapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.ruoyi.common.core.page.BaseMapperPlus; +import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus; import com.ruoyi.generator.domain.GenTable; import org.apache.ibatis.annotations.Param; diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java index 40f0738c..44baf825 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java @@ -1,7 +1,7 @@ package com.ruoyi.generator.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.generator.domain.GenTableColumn; import com.ruoyi.generator.mapper.GenTableColumnMapper; import org.springframework.stereotype.Service; @@ -15,7 +15,7 @@ import java.util.List; * @author ruoyi */ @Service -public class GenTableColumnServiceImpl extends ServiceImpl implements IGenTableColumnService { +public class GenTableColumnServiceImpl extends ServicePlusImpl implements IGenTableColumnService { /** * 查询业务字段列表 diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java index f156e4f5..4da27e87 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java @@ -1,17 +1,17 @@ package com.ruoyi.generator.service; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.exception.CustomException; +import com.ruoyi.common.utils.JsonUtils; import com.ruoyi.common.utils.PageUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.file.FileUtils; @@ -50,7 +50,7 @@ import java.util.zip.ZipOutputStream; */ @Slf4j @Service -public class GenTableServiceImpl extends ServiceImpl implements IGenTableService { +public class GenTableServiceImpl extends ServicePlusImpl implements IGenTableService { @Autowired private GenTableColumnMapper genTableColumnMapper; @@ -130,7 +130,7 @@ public class GenTableServiceImpl extends ServiceImpl i @Override @Transactional public void updateGenTable(GenTable genTable) { - String options = JSON.toJSONString(genTable.getParams()); + String options = JsonUtils.toJsonString(genTable.getParams()); genTable.setOptions(options); int row = baseMapper.updateById(genTable); if (row > 0) { @@ -263,12 +263,8 @@ public class GenTableServiceImpl extends ServiceImpl i StringWriter sw = new StringWriter(); Template tpl = Velocity.getTemplate(template, Constants.UTF8); tpl.merge(context, sw); - try { - String path = getGenPath(table, template); - FileUtils.writeStringToFile(new File(path), sw.toString(), Constants.UTF8); - } catch (IOException e) { - throw new CustomException("渲染模板失败,表名:" + table.getTableName()); - } + String path = getGenPath(table, template); + FileUtils.writeUtf8String(sw.toString(), path); } } } @@ -365,13 +361,12 @@ public class GenTableServiceImpl extends ServiceImpl i @Override public void validateEdit(GenTable genTable) { if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) { - String options = JSON.toJSONString(genTable.getParams()); - JSONObject paramsObj = JSONObject.parseObject(options); - if (Validator.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) { + Map paramsObj = genTable.getParams(); + if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_CODE))) { throw new CustomException("树编码字段不能为空"); - } else if (Validator.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) { + } else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_PARENT_CODE))) { throw new CustomException("树父编码字段不能为空"); - } else if (Validator.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) { + } else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_NAME))) { throw new CustomException("树名称字段不能为空"); } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) { if (Validator.isEmpty(genTable.getSubTableName())) { @@ -429,13 +424,13 @@ public class GenTableServiceImpl extends ServiceImpl i * @param genTable 设置后的生成对象 */ public void setTableFromOptions(GenTable genTable) { - JSONObject paramsObj = JSONObject.parseObject(genTable.getOptions()); + Map paramsObj = JsonUtils.parseMap(genTable.getOptions()); if (Validator.isNotNull(paramsObj)) { - String treeCode = paramsObj.getString(GenConstants.TREE_CODE); - String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); - String treeName = paramsObj.getString(GenConstants.TREE_NAME); - String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID); - String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); + String treeCode = Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)); + String treeParentCode = Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE)); + String treeName = Convert.toStr(paramsObj.get(GenConstants.TREE_NAME)); + String parentMenuId = Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID)); + String parentMenuName = Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_NAME)); genTable.setTreeCode(treeCode); genTable.setTreeParentCode(treeParentCode); @@ -448,7 +443,7 @@ public class GenTableServiceImpl extends ServiceImpl i /** * 获取代码生成地址 * - * @param table 业务表信息 + * @param table 业务表信息 * @param template 模板文件路径 * @return 生成地址 */ diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java index 22946f25..38d38540 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java @@ -1,6 +1,6 @@ package com.ruoyi.generator.service; -import com.ruoyi.common.core.page.IServicePlus; +import com.ruoyi.common.core.mybatisplus.core.IServicePlus; import com.ruoyi.generator.domain.GenTableColumn; import java.util.List; diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java index cbfeae4a..a44dd5e2 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java @@ -1,6 +1,6 @@ package com.ruoyi.generator.service; -import com.ruoyi.common.core.page.IServicePlus; +import com.ruoyi.common.core.mybatisplus.core.IServicePlus; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.generator.domain.GenTable; diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java index 283ba196..928a051f 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java @@ -11,7 +11,7 @@ import java.util.Arrays; /** * 代码生成器 工具类 - * + * * @author ruoyi */ public class GenUtils @@ -61,7 +61,7 @@ public class GenUtils column.setHtmlType(GenConstants.HTML_INPUT); // 如果是浮点型 统一用BigDecimal - String[] str = StrUtil.split(StrUtil.subBetween(column.getColumnType(), "(", ")"), ","); + String[] str = StrUtil.splitToArray(StrUtil.subBetween(column.getColumnType(), "(", ")"), ","); if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) { column.setJavaType(GenConstants.TYPE_BIGDECIMAL); @@ -132,7 +132,7 @@ public class GenUtils /** * 校验数组是否包含指定值 - * + * * @param arr 数组 * @param targetValue 值 * @return 是否包含 @@ -144,7 +144,7 @@ public class GenUtils /** * 获取模块名 - * + * * @param packageName 包名 * @return 模块名 */ @@ -158,7 +158,7 @@ public class GenUtils /** * 获取业务名 - * + * * @param tableName 表名 * @return 业务名 */ @@ -172,7 +172,7 @@ public class GenUtils /** * 表名转换成Java类名 - * + * * @param tableName 表名称 * @return 类名 */ @@ -182,7 +182,7 @@ public class GenUtils String tablePrefix = GenConfig.getTablePrefix(); if (autoRemovePre && StrUtil.isNotEmpty(tablePrefix)) { - String[] searchList = StrUtil.split(tablePrefix, ","); + String[] searchList = StrUtil.splitToArray(tablePrefix, ","); tableName = replaceFirst(tableName, searchList); } return StrUtil.upperFirst(StrUtil.toCamelCase(tableName)); @@ -190,7 +190,7 @@ public class GenUtils /** * 批量替换前缀 - * + * * @param replacementm 替换值 * @param searchList 替换列表 * @return @@ -211,7 +211,7 @@ public class GenUtils /** * 关键字替换 - * + * * @param text 需要被替换的名字 * @return 替换后的名字 */ @@ -222,7 +222,7 @@ public class GenUtils /** * 获取数据库类型字段 - * + * * @param columnType 列类型 * @return 截取后的列类型 */ @@ -240,7 +240,7 @@ public class GenUtils /** * 获取字段长度 - * + * * @param columnType 列类型 * @return 截取后的列类型 */ diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java index 69421cc0..76073c79 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java @@ -1,10 +1,11 @@ package com.ruoyi.generator.util; +import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSONObject; import com.ruoyi.common.constant.GenConstants; import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.JsonUtils; import com.ruoyi.generator.domain.GenTable; import com.ruoyi.generator.domain.GenTableColumn; import org.apache.velocity.VelocityContext; @@ -12,10 +13,11 @@ import org.apache.velocity.VelocityContext; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; /** * 模板处理工具类 - * + * * @author ruoyi */ public class VelocityUtils @@ -75,7 +77,7 @@ public class VelocityUtils public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) { String options = genTable.getOptions(); - JSONObject paramsObj = JSONObject.parseObject(options); + Map paramsObj = JsonUtils.parseMap(options); String parentMenuId = getParentMenuId(paramsObj); context.put("parentMenuId", parentMenuId); } @@ -83,7 +85,7 @@ public class VelocityUtils public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) { String options = genTable.getOptions(); - JSONObject paramsObj = JSONObject.parseObject(options); + Map paramsObj = JsonUtils.parseMap(options); String treeCode = getTreecode(paramsObj); String treeParentCode = getTreeParentCode(paramsObj); String treeName = getTreeName(paramsObj); @@ -94,11 +96,11 @@ public class VelocityUtils context.put("expandColumn", getExpandColumn(genTable)); if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { - context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE)); } if (paramsObj.containsKey(GenConstants.TREE_NAME)) { - context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); + context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME)); } } @@ -300,11 +302,11 @@ public class VelocityUtils * @param paramsObj 生成其他选项 * @return 上级菜单ID字段 */ - public static String getParentMenuId(JSONObject paramsObj) + public static String getParentMenuId(Map paramsObj) { if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)) { - return paramsObj.getString(GenConstants.PARENT_MENU_ID); + return Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID)); } return DEFAULT_PARENT_MENU_ID; } @@ -315,11 +317,11 @@ public class VelocityUtils * @param paramsObj 生成其他选项 * @return 树编码 */ - public static String getTreecode(JSONObject paramsObj) + public static String getTreecode(Map paramsObj) { - if (paramsObj.containsKey(GenConstants.TREE_CODE)) + if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) { - return StrUtil.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); + return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE))); } return StrUtil.EMPTY; } @@ -330,11 +332,11 @@ public class VelocityUtils * @param paramsObj 生成其他选项 * @return 树父编码 */ - public static String getTreeParentCode(JSONObject paramsObj) + public static String getTreeParentCode(Map paramsObj) { - if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { - return StrUtil.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE))); } return StrUtil.EMPTY; } @@ -345,11 +347,11 @@ public class VelocityUtils * @param paramsObj 生成其他选项 * @return 树名称 */ - public static String getTreeName(JSONObject paramsObj) + public static String getTreeName(Map paramsObj) { - if (paramsObj.containsKey(GenConstants.TREE_NAME)) + if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) { - return StrUtil.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); + return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_NAME))); } return StrUtil.EMPTY; } @@ -363,8 +365,8 @@ public class VelocityUtils public static int getExpandColumn(GenTable genTable) { String options = genTable.getOptions(); - JSONObject paramsObj = JSONObject.parseObject(options); - String treeName = paramsObj.getString(GenConstants.TREE_NAME); + Map paramsObj = JsonUtils.parseMap(options); + String treeName = Convert.toStr(paramsObj.get(GenConstants.TREE_NAME)); int num = 0; for (GenTableColumn column : genTable.getColumns()) { diff --git a/ruoyi-generator/src/main/resources/vm/java/addBo.java.vm b/ruoyi-generator/src/main/resources/vm/java/addBo.java.vm index e010d203..82d08627 100644 --- a/ruoyi-generator/src/main/resources/vm/java/addBo.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/addBo.java.vm @@ -2,7 +2,6 @@ package ${packageName}.bo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.util.Date; import javax.validation.constraints.*; @@ -27,9 +26,6 @@ public class ${ClassName}AddBo { /** $column.columnComment */ @ApiModelProperty("$column.columnComment") -#if($column.javaType == 'Date') - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") -#end #if($column.isRequired==1) #if($column.javaType == 'String') @NotBlank(message = "$column.columnComment不能为空") diff --git a/ruoyi-generator/src/main/resources/vm/java/domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm index b6eca827..5832be6c 100644 --- a/ruoyi-generator/src/main/resources/vm/java/domain.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm @@ -7,7 +7,6 @@ import lombok.experimental.Accessors; import java.io.Serializable; import java.util.Date; import java.math.BigDecimal; -import com.ruoyi.common.annotation.Excel; /** * ${functionName}对象 ${tableName} diff --git a/ruoyi-generator/src/main/resources/vm/java/editBo.java.vm b/ruoyi-generator/src/main/resources/vm/java/editBo.java.vm index 60b7e34c..3533a91e 100644 --- a/ruoyi-generator/src/main/resources/vm/java/editBo.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/editBo.java.vm @@ -2,7 +2,6 @@ package ${packageName}.bo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.util.Date; import javax.validation.constraints.*; @@ -26,9 +25,6 @@ public class ${ClassName}EditBo { /** $column.columnComment */ @ApiModelProperty("$column.columnComment") -#if($column.javaType == 'Date') - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") -#end #if($column.isRequired==1) #if($column.javaType == 'String') @NotBlank(message = "$column.columnComment不能为空") diff --git a/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm index 36a98642..6d4f40fb 100644 --- a/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm @@ -1,8 +1,8 @@ package ${packageName}.mapper; import ${packageName}.domain.${ClassName}; -import com.ruoyi.common.core.page.BaseMapperPlus; -import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache; +import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus; +import com.ruoyi.common.core.mybatisplus.cache.MybatisPlusRedisCache; import org.apache.ibatis.annotations.CacheNamespace; /** @@ -11,6 +11,7 @@ import org.apache.ibatis.annotations.CacheNamespace; * @author ${author} * @date ${datetime} */ +// 如使需切换数据源 请勿使用缓存 会造成数据不一致现象 @CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class) public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}> { diff --git a/ruoyi-generator/src/main/resources/vm/java/queryBo.java.vm b/ruoyi-generator/src/main/resources/vm/java/queryBo.java.vm index c7be0899..4c2b7eeb 100644 --- a/ruoyi-generator/src/main/resources/vm/java/queryBo.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/queryBo.java.vm @@ -50,9 +50,6 @@ public class ${ClassName}QueryBo extends ${Entity} { #foreach ($column in $columns) #if(!$table.isSuperColumn($column.javaField) && $column.query) /** $column.columnComment */ -#if($column.javaType == 'Date') - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") -#end @ApiModelProperty("$column.columnComment") private $column.javaType $column.javaField; #end diff --git a/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-generator/src/main/resources/vm/java/service.java.vm index d5016d43..9ae8620b 100644 --- a/ruoyi-generator/src/main/resources/vm/java/service.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -5,7 +5,7 @@ import ${packageName}.vo.${ClassName}Vo; import ${packageName}.bo.${ClassName}QueryBo; import ${packageName}.bo.${ClassName}AddBo; import ${packageName}.bo.${ClassName}EditBo; -import com.ruoyi.common.core.page.IServicePlus; +import com.ruoyi.common.core.mybatisplus.core.IServicePlus; #if($table.crud || $table.sub) import com.ruoyi.common.core.page.TableDataInfo; #end @@ -58,5 +58,5 @@ public interface I${ClassName}Service extends IServicePlus<${ClassName}> { * @param isValid 是否校验,true-删除前校验,false-不校验 * @return */ - Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid); } diff --git a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index c727ce6e..2421f310 100644 --- a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -8,7 +8,7 @@ import com.ruoyi.common.core.page.PagePlus; import com.ruoyi.common.core.page.TableDataInfo; #end import org.springframework.stereotype.Service; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import ${packageName}.bo.${ClassName}AddBo; @@ -30,7 +30,7 @@ import java.util.Collection; * @date ${datetime} */ @Service -public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service { +public class ${ClassName}ServiceImpl extends ServicePlusImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service { @Override public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){ @@ -101,7 +101,7 @@ public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${C } @Override - public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } diff --git a/ruoyi-generator/src/main/resources/vm/java/vo.java.vm b/ruoyi-generator/src/main/resources/vm/java/vo.java.vm index 4641d780..b07a7b4c 100644 --- a/ruoyi-generator/src/main/resources/vm/java/vo.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/vo.java.vm @@ -1,7 +1,6 @@ package ${packageName}.vo; import com.ruoyi.common.annotation.Excel; -import com.fasterxml.jackson.annotation.JsonFormat; #foreach ($import in $importList) import ${import}; #end @@ -41,7 +40,6 @@ public class ${ClassName}Vo { @Excel(name = "${comment}" , readConverterExp = "$column.readConverterExp()") #elseif($column.javaType == 'Date') @Excel(name = "${comment}" , width = 30, dateFormat = "yyyy-MM-dd") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") #else @Excel(name = "${comment}") #end diff --git a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm index 0b971b28..d43c2ce2 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -1,548 +1,546 @@ - - - + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index d6b311c1..abc799e2 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -174,7 +174,6 @@ #foreach($column in $columns) #set($field=$column.javaField) #if($column.insert && !$column.pk) -#if(($column.usableColumn) || (!$column.superColumn)) #set($parentheseIndex=$column.columnComment.indexOf("(")) #if($parentheseIndex != -1) #set($comment=$column.columnComment.substring(0, $parentheseIndex)) @@ -266,7 +265,6 @@ #end #end #end -#end #if($table.sub) ${subTable.functionName}信息 @@ -311,19 +309,19 @@ diff --git a/ruoyi-ui/src/assets/styles/ruoyi.scss b/ruoyi-ui/src/assets/styles/ruoyi.scss index de1db7f3..dee6d091 100644 --- a/ruoyi-ui/src/assets/styles/ruoyi.scss +++ b/ruoyi-ui/src/assets/styles/ruoyi.scss @@ -105,6 +105,15 @@ position: absolute; } +@media ( max-width : 768px) { + .pagination-container .el-pagination > .el-pagination__jump { + display: none !important; + } + .pagination-container .el-pagination > .el-pagination__sizes { + display: none !important; + } +} + .el-table .fixed-width .el-button--mini { padding-left: 0; padding-right: 0; diff --git a/ruoyi-ui/src/components/DictTag/index.vue b/ruoyi-ui/src/components/DictTag/index.vue new file mode 100644 index 00000000..47794150 --- /dev/null +++ b/ruoyi-ui/src/components/DictTag/index.vue @@ -0,0 +1,51 @@ + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/components/Pagination/index.vue b/ruoyi-ui/src/components/Pagination/index.vue index c815e132..5e5d8904 100644 --- a/ruoyi-ui/src/components/Pagination/index.vue +++ b/ruoyi-ui/src/components/Pagination/index.vue @@ -6,6 +6,7 @@ :page-size.sync="pageSize" :layout="layout" :page-sizes="pageSizes" + :pager-count="pagerCount" :total="total" v-bind="$attrs" @size-change="handleSizeChange" @@ -38,6 +39,11 @@ export default { return [10, 20, 30, 50] } }, + // 移动端页码按钮的数量端默认值5 + pagerCount: { + type: Number, + default: document.body.clientWidth < 992 ? 5 : 7 + }, layout: { type: String, default: 'total, sizes, prev, pager, next, jumper' diff --git a/ruoyi-ui/src/components/iFrame/index.vue b/ruoyi-ui/src/components/iFrame/index.vue new file mode 100644 index 00000000..4b85f365 --- /dev/null +++ b/ruoyi-ui/src/components/iFrame/index.vue @@ -0,0 +1,36 @@ +