同步ruoyi-vue-plus的2023-11-17至2023-11-23的更新

(1)fix 修复 OssFactory 并发多创建实例问题
   (2)update 优化 删除无用接口实现
   (3)!454 添加excel多sheet页导出
   (4)update 优化 重构 LoginHelper 将本地存储代码操作封装
   (5)update 优化 删除无用依赖
   (6)update 优化 删除无用注解
   (7)update 优化 更新用户异常提示 使用登录账号
   (8)fix 修复 token 失效后 登录获取用户null问题
   (9)fix 修复 session 多账号共用覆盖问题 改为 tokenSession 独立存储
   (10)add 新增 RedisUtils.setObjectIfExists 如果存在则设置方法
   (11)update transmittable-thread-local 2.14.2 => 2.14.4
   (12)update 优化 删除 hikaricp 官方不推荐使用的配置 jdbc4 协议自带校验方法
   (13)update 优化 开启 redisson 脚本缓存 减少网络传输
   (14)升级依赖update easyexcel 3.3.2 => 3.3.3
     update springboot-admin 3.1.7 => 3.1.8
     update aws-java-sdk-s3 1.12.540 => 1.12.600
   (15)细化oss配置管理权限控制,需要执行更新数据库脚本update.sql
   (16)update 优化 将 OSS配置 改为全局模式
   (17)fix 修复 excel合并注解会根据第一合并列的结果来决定后续的列合并
   (18)fix 修复 MybatisSystemException 空指针问题
   (19)update 优化 删除无用异常类
   (20)update 优化 验证码接口 增加限流配置
   (21)update 优化 丰富RedisUtils对List Set类型的操作
This commit is contained in:
dataprince 2023-12-25 16:16:02 +08:00
parent 64fea077c2
commit 64685e74f9
35 changed files with 282 additions and 270 deletions

View File

@ -30,7 +30,7 @@
<oshi.version>6.4.8</oshi.version> <oshi.version>6.4.8</oshi.version>
<commons.collections.version>3.2.2</commons.collections.version> <commons.collections.version>3.2.2</commons.collections.version>
<poi.version>5.2.3</poi.version> <poi.version>5.2.3</poi.version>
<easyexcel.version>3.3.2</easyexcel.version> <easyexcel.version>3.3.3</easyexcel.version>
<velocity.version>2.3</velocity.version> <velocity.version>2.3</velocity.version>
<jwt.version>0.9.1</jwt.version> <jwt.version>0.9.1</jwt.version>
<servlet-api.version>6.0.0</servlet-api.version> <servlet-api.version>6.0.0</servlet-api.version>
@ -46,13 +46,13 @@
<hutool.version>5.8.22</hutool.version> <hutool.version>5.8.22</hutool.version>
<redisson.version>3.25.1</redisson.version> <redisson.version>3.25.1</redisson.version>
<lock4j.version>2.2.4</lock4j.version> <lock4j.version>2.2.4</lock4j.version>
<alibaba-ttl.version>2.14.3</alibaba-ttl.version> <alibaba-ttl.version>2.14.4</alibaba-ttl.version>
<spring-boot-admin.version>3.1.7</spring-boot-admin.version> <spring-boot-admin.version>3.1.8</spring-boot-admin.version>
<powerjob.version>4.3.6</powerjob.version> <powerjob.version>4.3.6</powerjob.version>
<!-- 离线IP地址定位库 --> <!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version> <ip2region.version>2.7.0</ip2region.version>
<!-- OSS 配置 --> <!-- OSS 配置 -->
<aws-java-sdk-s3.version>1.12.540</aws-java-sdk-s3.version> <aws-java-sdk-s3.version>1.12.600</aws-java-sdk-s3.version>
<!-- 插件版本 --> <!-- 插件版本 -->
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version> <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>

View File

@ -62,7 +62,7 @@ public class AuthController {
* @return 结果 * @return 结果
*/ */
@PostMapping("/login") @PostMapping("/login")
public R<LoginVo> login(@Validated @RequestBody LoginBody loginBody) { public R<LoginVo> login(@RequestBody LoginBody loginBody) {
AjaxResult ajax = AjaxResult.success(); AjaxResult ajax = AjaxResult.success();
// 授权类型和客户端id // 授权类型和客户端id

View File

@ -6,8 +6,10 @@ import java.time.Duration;
import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.ruoyi.common.core.annotation.RateLimiter;
import com.ruoyi.common.core.constant.GlobalConstants; import com.ruoyi.common.core.constant.GlobalConstants;
import com.ruoyi.common.core.core.domain.AjaxResult; import com.ruoyi.common.core.core.domain.AjaxResult;
import com.ruoyi.common.core.enums.LimitType;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.reflect.ReflectUtils; import com.ruoyi.common.core.utils.reflect.ReflectUtils;
import com.ruoyi.common.core.utils.SpringUtils; import com.ruoyi.common.core.utils.SpringUtils;
@ -43,6 +45,7 @@ public class CaptchaController
/** /**
* 生成验证码 * 生成验证码
*/ */
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
@GetMapping("/captchaImage") @GetMapping("/captchaImage")
public AjaxResult getCode() { public AjaxResult getCode() {
CaptchaVo captchaVo = new CaptchaVo(); CaptchaVo captchaVo = new CaptchaVo();

View File

@ -161,6 +161,9 @@ public class SysLoginService {
public void logout() { public void logout() {
try { try {
LoginUser loginUser = LoginHelper.getLoginUser(); LoginUser loginUser = LoginHelper.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
return;
}
if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) { if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
// 超级管理员 登出清除动态租户 // 超级管理员 登出清除动态租户
TenantHelper.clearDynamic(); TenantHelper.clearDynamic();

View File

@ -15,8 +15,6 @@ spring:
idleTimeout: 600000 idleTimeout: 600000
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟 # 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟
maxLifetime: 1800000 maxLifetime: 1800000
# 连接测试query配置检测连接是否有效
connectionTestQuery: SELECT 1
# 多久检查一次连接的活性 # 多久检查一次连接的活性
keepaliveTime: 30000 keepaliveTime: 30000
mybatis-flex: mybatis-flex:
@ -93,7 +91,7 @@ redisson:
--- # 监控中心客户端配置 --- # 监控中心客户端配置
spring.boot.admin.client: spring.boot.admin.client:
# 增加客户端开关 # 增加客户端开关
enabled: false enabled: true
url: http://localhost:9090/admin url: http://localhost:9090/admin
instance: instance:
service-host-type: IP service-host-type: IP
@ -104,7 +102,7 @@ spring.boot.admin.client:
powerjob: powerjob:
worker: worker:
# 如何开启调度中心请查看文档教程 # 如何开启调度中心请查看文档教程
enabled: false enabled: true
# 需要先在 powerjob 登录页执行应用注册后才能使用 # 需要先在 powerjob 登录页执行应用注册后才能使用
app-name: ruoyi-worker app-name: ruoyi-worker
# 28080 端口 随着主应用端口飘逸 避免集群冲突 # 28080 端口 随着主应用端口飘逸 避免集群冲突
@ -112,6 +110,6 @@ powerjob:
protocol: http protocol: http
server-address: 127.0.0.1:7700 server-address: 127.0.0.1:7700
store-strategy: disk store-strategy: disk
enable-test-mode: false allow-lazy-connect-server: false
max-appended-wf-context-length: 4096 max-appended-wf-context-length: 4096
max-result-length: 4096 max-result-length: 4096

View File

@ -15,8 +15,6 @@ spring:
idleTimeout: 600000 idleTimeout: 600000
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟 # 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟
maxLifetime: 1800000 maxLifetime: 1800000
# 连接测试query配置检测连接是否有效
connectionTestQuery: SELECT 1
# 多久检查一次连接的活性 # 多久检查一次连接的活性
keepaliveTime: 30000 keepaliveTime: 30000
mybatis-flex: mybatis-flex:
@ -112,6 +110,6 @@ powerjob:
protocol: http protocol: http
server-address: 127.0.0.1:7700 server-address: 127.0.0.1:7700
store-strategy: disk store-strategy: disk
enable-test-mode: false allow-lazy-connect-server: false
max-appended-wf-context-length: 4096 max-appended-wf-context-length: 4096
max-result-length: 4096 max-result-length: 4096

View File

@ -125,12 +125,6 @@
<artifactId>hutool-extra</artifactId> <artifactId>hutool-extra</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
<scope>provided</scope>
</dependency>
<!-- mapstruct-plus --> <!-- mapstruct-plus -->
<dependency> <dependency>
<groupId>io.github.linpeilie</groupId> <groupId>io.github.linpeilie</groupId>

View File

@ -53,7 +53,7 @@ public interface CacheNames {
/** /**
* OSS配置 * OSS配置
*/ */
String SYS_OSS_CONFIG = "sys_oss_config"; String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
/** /**
* 在线用户 * 在线用户

View File

@ -1,15 +0,0 @@
package com.ruoyi.common.core.exception;
/**
* 演示模式异常
*
* @author ruoyi
*/
public class DemoModeException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public DemoModeException()
{
}
}

View File

@ -1,60 +0,0 @@
package com.ruoyi.common.core.exception;
import java.io.Serial;
/**
* 全局异常
*
* @author ruoyi
*/
public class GlobalException extends RuntimeException
{
@Serial
private static final long serialVersionUID = 1L;
/**
* 错误提示
*/
private String message;
/**
* 错误明细内部调试错误
*
*/
private String detailMessage;
/**
* 空构造方法避免反序列化问题
*/
public GlobalException()
{
}
public GlobalException(String message)
{
this.message = message;
}
public String getDetailMessage()
{
return detailMessage;
}
public GlobalException setDetailMessage(String detailMessage)
{
this.detailMessage = detailMessage;
return this;
}
@Override
public String getMessage()
{
return message;
}
public GlobalException setMessage(String message)
{
this.message = message;
return this;
}
}

View File

@ -1,29 +0,0 @@
package com.ruoyi.common.core.exception;
import java.io.Serial;
/**
* 工具类异常
*
* @author ruoyi
*/
public class UtilException extends RuntimeException
{
@Serial
private static final long serialVersionUID = 8247610319171014183L;
public UtilException(Throwable e)
{
super(e.getMessage(), e);
}
public UtilException(String message)
{
super(message);
}
public UtilException(String message, Throwable throwable)
{
super(message, throwable);
}
}

View File

@ -1,19 +0,0 @@
package com.ruoyi.common.core.exception.user;
import java.io.Serial;
/**
* 用户密码不正确或不符合规范异常类
*
* @author ruoyi
*/
public class UserPasswordNotMatchException extends UserException
{
@Serial
private static final long serialVersionUID = 1L;
public UserPasswordNotMatchException()
{
super("user.password.not.match", (Object)null);
}
}

View File

@ -1,19 +0,0 @@
package com.ruoyi.common.core.exception.user;
import java.io.Serial;
/**
* 用户错误最大次数异常类
*
* @author ruoyi
*/
public class UserPasswordRetryLimitExceedException extends UserException
{
@Serial
private static final long serialVersionUID = 1L;
public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime)
{
super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime });
}
}

View File

@ -76,7 +76,6 @@ import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.ruoyi.common.core.config.RuoYiConfig; import com.ruoyi.common.core.config.RuoYiConfig;
import com.ruoyi.common.core.exception.UtilException;
import com.ruoyi.common.core.utils.DateUtils; import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.file.FileTypeUtils; import com.ruoyi.common.core.utils.file.FileTypeUtils;
import com.ruoyi.common.core.utils.file.FileUtils; import com.ruoyi.common.core.utils.file.FileUtils;
@ -540,7 +539,7 @@ public class ExcelUtil<T> {
return AjaxResult.success(filename); return AjaxResult.success(filename);
} catch (Exception e) { } catch (Exception e) {
log.error("导出Excel异常{}", e.getMessage()); log.error("导出Excel异常{}", e.getMessage());
throw new UtilException("导出Excel失败请联系网站管理员"); throw new IllegalArgumentException("导出Excel失败请联系网站管理员");
} finally { } finally {
IOUtils.closeQuietly(wb); IOUtils.closeQuietly(wb);
IOUtils.closeQuietly(out); IOUtils.closeQuietly(out);

View File

@ -1,7 +1,6 @@
package com.ruoyi.common.core.utils.sql; package com.ruoyi.common.core.utils.sql;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.exception.UtilException;
/** /**
* sql操作工具类 * sql操作工具类
@ -32,11 +31,11 @@ public class SqlUtil
{ {
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
{ {
throw new UtilException("参数不符合规范,不能进行查询"); throw new IllegalArgumentException("参数不符合规范,不能进行查询");
} }
if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH)
{ {
throw new UtilException("参数已超过最大限制,不能进行查询"); throw new IllegalArgumentException("参数已超过最大限制,不能进行查询");
} }
return value; return value;
} }
@ -63,7 +62,7 @@ public class SqlUtil
{ {
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1)
{ {
throw new UtilException("参数存在SQL注入风险"); throw new IllegalArgumentException("参数存在SQL注入风险");
} }
} }
} }

View File

@ -5,7 +5,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import com.ruoyi.common.core.exception.UtilException;
/** /**
* 提供通用唯一识别码universally unique identifierUUID实现 * 提供通用唯一识别码universally unique identifierUUID实现
@ -467,7 +466,7 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
} }
catch (NoSuchAlgorithmException e) catch (NoSuchAlgorithmException e)
{ {
throw new UtilException(e); throw new IllegalArgumentException(e);
} }
} }

View File

@ -98,6 +98,26 @@ public class CellMergeStrategy extends AbstractMergeStrategy {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
} }
map.put(field, new RepeatCell(val, i)); map.put(field, new RepeatCell(val, i));
} else if (j == 0) {
if (i == list.size() - 1) {
if (i > repeatCell.getCurrent()) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
}
}
} else {
// 判断前面的是否合并了
RepeatCell firstCell = map.get(mergeFields.get(0));
if (repeatCell.getCurrent() != firstCell.getCurrent()) {
if (i == list.size() - 1) {
if (i > repeatCell.getCurrent()) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
}
} else if (repeatCell.getCurrent() < firstCell.getCurrent()) {
if (i - repeatCell.getCurrent() > 1) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
}
} else if (i == list.size() - 1) { } else if (i == list.size() - 1) {
if (i > repeatCell.getCurrent()) { if (i > repeatCell.getCurrent()) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
@ -106,6 +126,7 @@ public class CellMergeStrategy extends AbstractMergeStrategy {
} }
} }
} }
}
return cellList; return cellList;
} }

View File

@ -269,6 +269,27 @@ public class ExcelUtil {
} }
} }
/**
* 多sheet模板导出 模板格式为 {key.属性}
*
* @param filename 文件名
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
* 例如: excel/temp.xlsx
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
* @param data 模板需要的数据
* @param response 响应体
*/
public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String filename, String templatePath, HttpServletResponse response) {
try {
resetResponse(filename, response);
ServletOutputStream os = response.getOutputStream();
exportTemplateMultiSheet(data, templatePath, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/** /**
* 多表多数据模板导出 模板格式为 {key.属性} * 多表多数据模板导出 模板格式为 {key.属性}
* *
@ -303,6 +324,42 @@ public class ExcelUtil {
excelWriter.finish(); excelWriter.finish();
} }
/**
* 多sheet模板导出 模板格式为 {key.属性}
*
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
* 例如: excel/temp.xlsx
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
* @param data 模板需要的数据
* @param os 输出流
*/
public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) {
ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = EasyExcel.write(os)
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
if (CollUtil.isEmpty(data)) {
throw new IllegalArgumentException("数据为空");
}
for (int i = 0; i < data.size(); i++) {
WriteSheet writeSheet = EasyExcel.writerSheet(i).build();
for (Map.Entry<String, Object> map : data.get(i).entrySet()) {
// 设置列表后续还有数据
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
if (map.getValue() instanceof Collection) {
// 多表导出必须使用 FillWrapper
excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
} else {
excelWriter.fill(map.getValue(), writeSheet);
}
}
}
excelWriter.finish();
}
/** /**
* 重置响应体 * 重置响应体
*/ */

View File

@ -26,7 +26,7 @@ public class OperLogEvent implements Serializable {
/** /**
* 租户ID * 租户ID
*/ */
private String tenantId; private Long tenantId;
/** /**
* 操作模块 * 操作模块

View File

@ -35,7 +35,7 @@ public class MybatisExceptionHandler {
public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
String message = e.getMessage(); String message = e.getMessage();
if (message.contains("CannotFindDataSourceException")) { if ("CannotFindDataSourceException".contains(message)) {
log.error("请求地址'{}', 未找到数据源", requestURI); log.error("请求地址'{}', 未找到数据源", requestURI);
return R.fail("未找到数据源,请联系管理员确认"); return R.fail("未找到数据源,请联系管理员确认");
} }

View File

@ -16,6 +16,11 @@
</description> </description>
<dependencies> <dependencies>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-json</artifactId> <artifactId>ruoyi-common-json</artifactId>

View File

@ -1,5 +1,7 @@
package com.ruoyi.common.oss.constant; package com.ruoyi.common.oss.constant;
import com.ruoyi.common.core.constant.GlobalConstants;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -13,7 +15,7 @@ public interface OssConstant {
/** /**
* 默认配置KEY * 默认配置KEY
*/ */
String DEFAULT_CONFIG_KEY = "sys_oss:default_config"; String DEFAULT_CONFIG_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss:default_config";
/** /**
* 预览列表资源开关Key * 预览列表资源开关Key

View File

@ -39,7 +39,7 @@ public class OssFactory {
/** /**
* 根据类型获取实例 * 根据类型获取实例
*/ */
public static OssClient instance(String configKey) { public static synchronized OssClient instance(String configKey) {
String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey); String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);
if (json == null) { if (json == null) {
throw new OssException("系统异常, '" + configKey + "'配置信息不存在!"); throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");

View File

@ -49,6 +49,8 @@ public class RedisConfig {
CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec); CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);
config.setThreads(redissonProperties.getThreads()) config.setThreads(redissonProperties.getThreads())
.setNettyThreads(redissonProperties.getNettyThreads()) .setNettyThreads(redissonProperties.getNettyThreads())
// 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现)
.setUseScriptCache(true)
.setCodec(codec); .setCodec(codec);
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
if (ObjectUtil.isNotNull(singleServerConfig)) { if (ObjectUtil.isNotNull(singleServerConfig)) {

View File

@ -142,6 +142,18 @@ public class RedisUtils {
return bucket.setIfAbsent(value, duration); return bucket.setIfAbsent(value, duration);
} }
/**
* 如果存在则设置 并返回 true 如果存在则返回 false
*
* @param key 缓存的键值
* @param value 缓存的值
* @return set成功或失败
*/
public static <T> boolean setObjectIfExists(final String key, final T value, final Duration duration) {
RBucket<T> bucket = CLIENT.getBucket(key);
return bucket.setIfExists(value, duration);
}
/** /**
* 注册对象监听器 * 注册对象监听器
* <p> * <p>
@ -243,6 +255,18 @@ public class RedisUtils {
return rList.addAll(dataList); return rList.addAll(dataList);
} }
/**
* 追加缓存List数据
*
* @param key 缓存的键值
* @param data 待缓存的数据
* @return 缓存的对象
*/
public static <T> boolean addCacheList(final String key, final T data) {
RList<T> rList = CLIENT.getList(key);
return rList.add(data);
}
/** /**
* 注册List监听器 * 注册List监听器
* <p> * <p>
@ -267,6 +291,19 @@ public class RedisUtils {
return rList.readAll(); return rList.readAll();
} }
/**
* 获得缓存的list对象(范围)
*
* @param key 缓存的键值
* @param form 起始下标
* @param to 截止下标
* @return 缓存键值对应的数据
*/
public static <T> List<T> getCacheListRange(final String key, int form, int to) {
RList<T> rList = CLIENT.getList(key);
return rList.range(form, to);
}
/** /**
* 缓存Set * 缓存Set
* *
@ -279,6 +316,18 @@ public class RedisUtils {
return rSet.addAll(dataSet); return rSet.addAll(dataSet);
} }
/**
* 追加缓存Set数据
*
* @param key 缓存的键值
* @param data 待缓存的数据
* @return 缓存的对象
*/
public static <T> boolean addCacheSet(final String key, final T data) {
RSet<T> rSet = CLIENT.getSet(key);
return rSet.add(data);
}
/** /**
* 注册Set监听器 * 注册Set监听器
* <p> * <p>

View File

@ -19,7 +19,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
*/ */
@AutoConfiguration @AutoConfiguration
@PropertySource(value = "classpath:common-satoken.yml", factory = YmlPropertySourceFactory.class) @PropertySource(value = "classpath:common-satoken.yml", factory = YmlPropertySourceFactory.class)
public class SaTokenConfig implements WebMvcConfigurer { public class SaTokenConfig {
@Bean @Bean
public StpLogic getStpLogicJwt() { public StpLogic getStpLogicJwt() {

View File

@ -45,12 +45,9 @@ public class FlexSaTokenDao implements SaTokenDao {
*/ */
@Override @Override
public void update(String key, String value) { public void update(String key, String value) {
long expire = getTimeout(key); if (RedisUtils.hasKey(key)) {
// -2 = 无此键 RedisUtils.setCacheObject(key, value, true);
if (expire == NOT_VALUE_EXPIRE) {
return;
} }
this.set(key, value, expire);
} }
/** /**
@ -75,17 +72,6 @@ public class FlexSaTokenDao implements SaTokenDao {
*/ */
@Override @Override
public void updateTimeout(String key, long timeout) { public void updateTimeout(String key, long timeout) {
// 判断是否想要设置为永久
if (timeout == NEVER_EXPIRE) {
long expire = getTimeout(key);
if (expire == NEVER_EXPIRE) {
// 如果其已经被设置为永久则不作任何处理
} else {
// 如果尚未被设置为永久那么再次set一次
this.set(key, this.get(key), timeout);
}
return;
}
RedisUtils.expire(key, Duration.ofSeconds(timeout)); RedisUtils.expire(key, Duration.ofSeconds(timeout));
} }
@ -119,12 +105,9 @@ public class FlexSaTokenDao implements SaTokenDao {
*/ */
@Override @Override
public void updateObject(String key, Object object) { public void updateObject(String key, Object object) {
long expire = getObjectTimeout(key); if (RedisUtils.hasKey(key)) {
// -2 = 无此键 RedisUtils.setCacheObject(key, object, true);
if (expire == NOT_VALUE_EXPIRE) {
return;
} }
this.setObject(key, object, expire);
} }
/** /**
@ -149,17 +132,6 @@ public class FlexSaTokenDao implements SaTokenDao {
*/ */
@Override @Override
public void updateObjectTimeout(String key, long timeout) { public void updateObjectTimeout(String key, long timeout) {
// 判断是否想要设置为永久
if (timeout == NEVER_EXPIRE) {
long expire = getObjectTimeout(key);
if (expire == NEVER_EXPIRE) {
// 如果其已经被设置为永久则不作任何处理
} else {
// 如果尚未被设置为永久那么再次set一次
this.setObject(key, this.getObject(key), timeout);
}
return;
}
RedisUtils.expire(key, Duration.ofSeconds(timeout)); RedisUtils.expire(key, Duration.ofSeconds(timeout));
} }

View File

@ -11,7 +11,6 @@ import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException; import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import com.ruoyi.common.core.core.domain.R; import com.ruoyi.common.core.core.domain.R;
import com.ruoyi.common.core.exception.DemoModeException;
import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.StreamUtils; import com.ruoyi.common.core.utils.StreamUtils;
import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.context.support.DefaultMessageSourceResolvable;
@ -161,12 +160,4 @@ public class GlobalExceptionHandler {
String message = e.getBindingResult().getFieldError().getDefaultMessage(); String message = e.getBindingResult().getFieldError().getDefaultMessage();
return R.fail(message); return R.fail(message);
} }
/**
* 演示模式异常
*/
@ExceptionHandler(DemoModeException.class)
public R<Void> handleDemoModeException(DemoModeException e) {
return R.fail("演示模式,不允许操作");
}
} }

View File

@ -15,6 +15,7 @@ import com.ruoyi.common.core.core.domain.model.LoginUser;
import com.ruoyi.common.core.enums.UserType; import com.ruoyi.common.core.enums.UserType;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
/** /**
* 登录鉴权助手 * 登录鉴权助手
@ -36,6 +37,7 @@ public class LoginHelper {
public static final String USER_KEY = "userId"; public static final String USER_KEY = "userId";
public static final String DEPT_KEY = "deptId"; public static final String DEPT_KEY = "deptId";
public static final String CLIENT_KEY = "clientid"; public static final String CLIENT_KEY = "clientid";
public static final String TENANT_ADMIN_KEY = "isTenantAdmin";
/** /**
* 登录系统 基于 设备类型 * 登录系统 基于 设备类型
@ -56,32 +58,45 @@ public class LoginHelper {
model.setExtra(TENANT_KEY, loginUser.getTenantId()) model.setExtra(TENANT_KEY, loginUser.getTenantId())
.setExtra(USER_KEY, loginUser.getUserId()) .setExtra(USER_KEY, loginUser.getUserId())
.setExtra(DEPT_KEY, loginUser.getDeptId())); .setExtra(DEPT_KEY, loginUser.getDeptId()));
StpUtil.getSession().set(LOGIN_USER_KEY, loginUser); //StpUtil.getSession().set(LOGIN_USER_KEY, loginUser);
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
} }
/** /**
* 获取用户(多级缓存) * 获取用户(多级缓存)
*/ */
public static LoginUser getLoginUser() { public static LoginUser getLoginUser() {
LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY); // LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);
if (loginUser != null) { // if (loginUser != null) {
return loginUser; // return loginUser;
} // }
SaSession session = StpUtil.getSession(); // SaSession session = StpUtil.getSession();
// if (ObjectUtil.isNull(session)) {
// return null;
// }
// loginUser = (LoginUser) session.get(LOGIN_USER_KEY);
// SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
// return loginUser;
return (LoginUser) getStorageIfAbsentSet(LOGIN_USER_KEY, () -> {
SaSession session = StpUtil.getTokenSession();
if (ObjectUtil.isNull(session)) { if (ObjectUtil.isNull(session)) {
return null; return null;
} }
loginUser = (LoginUser) session.get(LOGIN_USER_KEY); return session.get(LOGIN_USER_KEY);
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); });
return loginUser;
} }
/** /**
* 获取用户基于token * 获取用户基于token
*/ */
public static LoginUser getLoginUser(String token) { public static LoginUser getLoginUser(String token) {
Object loginId = StpUtil.getLoginIdByToken(token); // Object loginId = StpUtil.getLoginIdByToken(token);
SaSession session = StpUtil.getSessionByLoginId(loginId); // SaSession session = StpUtil.getSessionByLoginId(loginId);
// if (ObjectUtil.isNull(session)) {
// return null;
// }
// return (LoginUser) session.get(LOGIN_USER_KEY);
SaSession session = StpUtil.getTokenSessionByToken(token);
if (ObjectUtil.isNull(session)) { if (ObjectUtil.isNull(session)) {
return null; return null;
} }
@ -98,8 +113,8 @@ public class LoginHelper {
/** /**
* 获取租户ID * 获取租户ID
*/ */
public static String getTenantId() { public static Long getTenantId() {
return Convert.toStr(getExtra(TENANT_KEY)); return Convert.toLong(getExtra(TENANT_KEY));
} }
/** /**
@ -110,17 +125,18 @@ public class LoginHelper {
} }
private static Object getExtra(String key) { private static Object getExtra(String key) {
Object obj; // Object obj;
try { // try {
obj = SaHolder.getStorage().get(key); // obj = SaHolder.getStorage().get(key);
if (ObjectUtil.isNull(obj)) { // if (ObjectUtil.isNull(obj)) {
obj = StpUtil.getExtra(key); // obj = StpUtil.getExtra(key);
SaHolder.getStorage().set(key, obj); // SaHolder.getStorage().set(key, obj);
} // }
} catch (Exception e) { // } catch (Exception e) {
return null; // return null;
} // }
return obj; // return obj;
return getStorageIfAbsentSet(key, () -> StpUtil.getExtra(key));
} }
/** /**
@ -163,7 +179,27 @@ public class LoginHelper {
} }
public static boolean isTenantAdmin() { public static boolean isTenantAdmin() {
Object value = getStorageIfAbsentSet(TENANT_ADMIN_KEY, () -> {
return isTenantAdmin(getLoginUser().getRolePermission()); return isTenantAdmin(getLoginUser().getRolePermission());
});
return Convert.toBool(value);
}
public static boolean isLogin() {
return getLoginUser() != null;
}
public static Object getStorageIfAbsentSet(String key, Supplier<Object> handle) {
try {
Object obj = SaHolder.getStorage().get(key);
if (ObjectUtil.isNull(obj)) {
obj = handle.get();
SaHolder.getStorage().set(key, obj);
}
return obj;
} catch (Exception e) {
return null;
}
} }
} }

View File

@ -24,7 +24,7 @@ public class PlusTenantLineHandler {
private final TenantProperties tenantProperties; private final TenantProperties tenantProperties;
public Expression getTenantId() { public Expression getTenantId() {
String tenantId = LoginHelper.getTenantId(); String tenantId = LoginHelper.getTenantId().toString();
if (StringUtils.isBlank(tenantId)) { if (StringUtils.isBlank(tenantId)) {
return new NullValue(); return new NullValue();
} }
@ -38,7 +38,7 @@ public class PlusTenantLineHandler {
} }
public boolean ignoreTable(String tableName) { public boolean ignoreTable(String tableName) {
String tenantId = LoginHelper.getTenantId(); String tenantId = LoginHelper.getTenantId().toString();
// 判断是否有租户 // 判断是否有租户
if (StringUtils.isNotBlank(tenantId)) { if (StringUtils.isNotBlank(tenantId)) {
// 不需要过滤租户的表 // 不需要过滤租户的表

View File

@ -130,7 +130,7 @@ public class TenantHelper {
public static String getTenantId() { public static String getTenantId() {
String tenantId = TenantHelper.getDynamic(); String tenantId = TenantHelper.getDynamic();
if (StringUtils.isBlank(tenantId)) { if (StringUtils.isBlank(tenantId)) {
tenantId = LoginHelper.getTenantId(); tenantId = LoginHelper.getTenantId().toString();
} }
return tenantId; return tenantId;
} }

View File

@ -43,7 +43,7 @@ public class SysOssConfigController extends BaseController {
/** /**
* 查询对象存储配置列表 * 查询对象存储配置列表
*/ */
@SaCheckPermission("system:oss:list") @SaCheckPermission("system:ossConfig:list")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo<SysOssConfigVo> list(SysOssConfigBo bo) { public TableDataInfo<SysOssConfigVo> list(SysOssConfigBo bo) {
return ossConfigService.queryPageList(bo); return ossConfigService.queryPageList(bo);
@ -54,7 +54,7 @@ public class SysOssConfigController extends BaseController {
* *
* @param ossConfigId OSS配置ID * @param ossConfigId OSS配置ID
*/ */
@SaCheckPermission("system:oss:query") @SaCheckPermission("system:ossConfig:list")
@GetMapping("/{ossConfigId}") @GetMapping("/{ossConfigId}")
public R<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空") public R<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long ossConfigId) { @PathVariable Long ossConfigId) {
@ -64,7 +64,7 @@ public class SysOssConfigController extends BaseController {
/** /**
* 新增对象存储配置 * 新增对象存储配置
*/ */
@SaCheckPermission("system:oss:add") @SaCheckPermission("system:ossConfig:add")
@Log(title = "对象存储配置", businessType = BusinessType.INSERT) @Log(title = "对象存储配置", businessType = BusinessType.INSERT)
@RepeatSubmit() @RepeatSubmit()
@PostMapping() @PostMapping()
@ -79,7 +79,7 @@ public class SysOssConfigController extends BaseController {
/** /**
* 修改对象存储配置 * 修改对象存储配置
*/ */
@SaCheckPermission("system:oss:edit") @SaCheckPermission("system:ossConfig:edit")
@Log(title = "对象存储配置", businessType = BusinessType.UPDATE) @Log(title = "对象存储配置", businessType = BusinessType.UPDATE)
@RepeatSubmit() @RepeatSubmit()
@PutMapping() @PutMapping()
@ -96,7 +96,7 @@ public class SysOssConfigController extends BaseController {
* *
* @param ossConfigIds OSS配置ID串 * @param ossConfigIds OSS配置ID串
*/ */
@SaCheckPermission("system:oss:remove") @SaCheckPermission("system:ossConfig:remove")
@Log(title = "对象存储配置", businessType = BusinessType.DELETE) @Log(title = "对象存储配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ossConfigIds}") @DeleteMapping("/{ossConfigIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") public R<Void> remove(@NotEmpty(message = "主键不能为空")
@ -111,7 +111,7 @@ public class SysOssConfigController extends BaseController {
/** /**
* 状态修改 * 状态修改
*/ */
@SaCheckPermission("system:oss:edit") @SaCheckPermission("system:ossConfig:edit")
@Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE) @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus") @PutMapping("/changeStatus")
public R<Void> changeStatus(@RequestBody SysOssConfigBo bo) { public R<Void> changeStatus(@RequestBody SysOssConfigBo bo) {

View File

@ -72,16 +72,17 @@ public class SysProfileController extends BaseController
public R<Void> updateProfile(@RequestBody SysUserProfileBo profile) public R<Void> updateProfile(@RequestBody SysUserProfileBo profile)
{ {
SysUserBo user = BeanUtil.toBean(profile, SysUserBo.class); SysUserBo user = BeanUtil.toBean(profile, SysUserBo.class);
String username = LoginHelper.getUsername();
LoginUser loginUser = LoginHelper.getLoginUser(); LoginUser loginUser = LoginHelper.getLoginUser();
SysUserVo sysUser = userService.selectUserById(loginUser.getUserId()); SysUserVo sysUser = userService.selectUserById(loginUser.getUserId());
user.setUserName(sysUser.getUserName()); user.setUserName(sysUser.getUserName());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{ {
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); return R.fail("修改用户'" + username + "'失败,手机号码已存在");
} }
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{ {
return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); return R.fail("修改用户'" + username + "'失败,邮箱账号已存在");
} }
user.setUserId(sysUser.getUserId()); user.setUserId(sysUser.getUserId());
// user.setPassword(null); // user.setPassword(null);

View File

@ -33,3 +33,16 @@ UPDATE `pj_job_info` SET `processor_info`='com.ruoyi.job.processors.StandalonePr
UPDATE `pj_job_info` SET `processor_info`='com.ruoyi.job.processors.BroadcastProcessorDemo' WHERE `id`=2; UPDATE `pj_job_info` SET `processor_info`='com.ruoyi.job.processors.BroadcastProcessorDemo' WHERE `id`=2;
UPDATE `pj_job_info` SET `processor_info`='com.ruoyi.job.processors.MapProcessorDemo' WHERE `id`=3; UPDATE `pj_job_info` SET `processor_info`='com.ruoyi.job.processors.MapProcessorDemo' WHERE `id`=3;
UPDATE `pj_job_info` SET `processor_info`='com.ruoyi.job.processors.MapReduceProcessorDemo' WHERE `id`=4; UPDATE `pj_job_info` SET `processor_info`='com.ruoyi.job.processors.MapReduceProcessorDemo' WHERE `id`=4;
--
ALTER TABLE `sys_menu`
CHANGE COLUMN `update_by` `update_by` BIGINT(19) NULL DEFAULT '0' COMMENT '更新者' AFTER `create_time`;
delete from sys_menu where menu_id in (1604, 1605);
insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:query', '#', 1, sysdate(), null, null, '');
insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:upload', '#', 1, sysdate(), null, null, '');
insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:download', '#', 1, sysdate(), null, null, '');
insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:remove', '#', 1, sysdate(), null, null, '');
insert into sys_menu values('1620', '配置列表', '118', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:list', '#', 1, sysdate(), null, null, '');
insert into sys_menu values('1621', '配置添加', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:add', '#', 1, sysdate(), null, null, '');
insert into sys_menu values('1622', '配置编辑', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:edit', '#', 1, sysdate(), null, null, '');
insert into sys_menu values('1623', '配置删除', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:remove', '#', 1, sysdate(), null, null, '');

View File

@ -41,3 +41,15 @@ ALTER COLUMN "node_params" TYPE TEXT,
ALTER COLUMN "node_params" DROP NOT NULL, ALTER COLUMN "node_params" DROP NOT NULL,
ALTER COLUMN "node_params" DROP DEFAULT; ALTER COLUMN "node_params" DROP DEFAULT;
COMMENT ON COLUMN "pj_workflow_node_info"."node_params" IS ''; COMMENT ON COLUMN "pj_workflow_node_info"."node_params" IS '';
--
delete from sys_menu where menu_id in (1604, 1605);
insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:query', '#', 1, now(), null, null, '');
insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:upload', '#', 1, now(), null, null, '');
insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:download', '#', 1, now(), null, null, '');
insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:remove', '#', 1, now(), null, null, '');
insert into sys_menu values('1620', '配置列表', '118', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:list', '#', 1, now(), null, null, '');
insert into sys_menu values('1621', '配置添加', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:add', '#', 1, now(), null, null, '');
insert into sys_menu values('1622', '配置编辑', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:edit', '#', 1, now(), null, null, '');
insert into sys_menu values('1623', '配置删除', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:remove', '#', 1, now(), null, null, '');