Compare commits

...

31 Commits

Author SHA1 Message Date
xiaozhi bb68d9e227 Merge remote-tracking branch 'origin/future/6.X' into future/6.X 2026-06-13 18:02:17 +08:00
lau b177620e66 fix 客户端导出报错修复,暂不支持List字段导出 2026-06-13 18:02:12 +08:00
疯狂的狮子Li bd57356912 update springboot 4.1.0
update springdoc 3.0.3
update fesod 2.0.2
update hutool 5.8.46
update redisson 4.5.0
update mica-mqtt 2.6.5
2026-06-12 09:27:35 +08:00
疯狂的狮子Li d50631575a update springboot 4.1.0
update springdoc 3.0.3
update fesod 2.0.2
update hutool 5.8.46
update redisson 4.5.0
update mica-mqtt 2.6.5
2026-06-11 14:48:47 +08:00
AprilWind 0cab98bc8a !859 update 优化日期处理方法,简化时间格式化逻辑
* update 优化日期处理方法,简化时间格式化逻辑
2026-06-11 06:26:50 +00:00
lau 415a72b7c6 add 未知异常和系统异常增加随机编号方便查找 2026-06-10 17:19:51 +08:00
lau 4498b9c9d5 add 新增上传附件保存扩展信息 2026-06-10 17:08:49 +08:00
AprilWind 0b37392a8f !856 update 优化代码,简化类型检查
* update 优化代码,简化类型检查
2026-06-10 06:38:46 +00:00
AprilWind fc43e368b7 update 升级bouncycastle版本至1.84 2026-06-10 11:34:44 +08:00
AprilWind e38e99b7f8 !854 update 优化使用getFirst()替代get(0)以提高代码可读性
* update 优化使用getFirst()替代get(0)以提高代码可读性
2026-06-10 02:18:35 +00:00
疯狂的狮子Li 83eb74c56d fix 修复 代码生成默认菜单上级id错误 2026-06-10 09:37:26 +08:00
疯狂的狮子Li a87dcea74b update 优化 增加snailai-server相关docker配置 2026-06-09 11:07:07 +08:00
疯狂的狮子Li 060da45f84 update 优化 登录异常提示 2026-06-09 10:30:05 +08:00
疯狂的狮子Li 1cb5d841d9 update 恢复前端异常返回内容 2026-06-09 10:22:07 +08:00
AprilWind 00a1c1037c update 增加对客户端ID与Token不匹配的错误信息处理 2026-06-09 09:51:06 +08:00
AprilWind 9e192f5197 update 优化NotLoginException处理,提供更详细的错误信息 2026-06-09 09:30:56 +08:00
疯狂的狮子Li 4875594bcb update 优化 代码生成前端模板 2026-06-08 18:48:14 +08:00
疯狂的狮子Li 90f8142d0b update 优化代码小优化项
- 抄送历史任务为空时改为明确的 ServiceException;当前任务办理人查询改为 ID 去重后批量远程查
- 流程图提示里用户 ID 去重,部门名称从循环内逐条Dubbo 调用改为一次批量查询
2026-06-08 17:26:32 +08:00
疯狂的狮子Li 3a6d62d695 fix 修复 一些发现的小bug
- 短信验证码限流 key 改为 #phoneNumber,验证码改为发送成功后再写 Redis,失败消息避免 NPE
- 邮箱验证码改为邮件发送成功后再写 Redis
- ValidatorUtils 增加 NPE 校验
- 部门导入路径不存在时明确报错,不再静默转成 null
- 重复提交切面完整扫描数组、集合、Map 中的过滤对象
- Excel 级联下拉补齐一级选项的 Excel 名称规则校验和重复校验
2026-06-08 16:37:18 +08:00
疯狂的狮子Li 14f96acd60 update 排除本地配置文件 2026-06-08 12:01:57 +08:00
疯狂的狮子Li 0ca43166d2 update snail-ai 升级 0.0.4 2026-06-08 09:27:41 +08:00
疯狂的狮子Li c64fb7a16a update 重构 头像上传改为先上传oss后更新用户数据 2026-06-05 16:41:12 +08:00
疯狂的狮子Li 9e21304dbf update 优化 防止工作流保存被xss过滤 2026-06-05 15:01:07 +08:00
疯狂的狮子Li 9888150523 update 更新系统标题和描述 2026-06-04 17:19:50 +08:00
疯狂的狮子Li 02cf3546fb update mysql 8.0.42 => 8.4.9
update nginx 1.23.4 => 1.31.1
update redis 7.2.8 => 8.6.3
update minio RELEASE.2026-04-17T00-00-00Z
2026-06-04 17:13:20 +08:00
疯狂的狮子Li 2d4d7b6fb1 update 格式化全部代码 2026-06-04 16:08:04 +08:00
AprilWind e08160afb6 update 优化冗余分隔符 2026-06-04 15:08:13 +08:00
AprilWind f52bb99419 add 新增spring ai docker run配置 2026-06-04 14:28:35 +08:00
AprilWind d20e03ea61 update 优化spring ai名称 2026-06-04 14:15:44 +08:00
疯狂的狮子Li 8c36b41072 update 回滚错误修改 2026-06-04 13:04:01 +08:00
AprilWind 94d7cd3489 update 优化媒体工具类,添加私有构造函数以防止实例化 2026-06-04 12:19:41 +08:00
238 changed files with 1112 additions and 1461 deletions
+3
View File
@@ -50,3 +50,6 @@ nbdist/
!*/build/*.xml !*/build/*.xml
.flattened-pom.xml .flattened-pom.xml
application-local.yml
application-ga.yml
+13
View File
@@ -0,0 +1,13 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ruoyi-snailai-server" type="docker-deploy" factoryName="dockerfile"
server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-snailai-server:5.5.3"/>
<option name="buildOnly" value="true"/>
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailai-server/Dockerfile"/>
</settings>
</deployment>
<method v="2"/>
</configuration>
</component>
+1 -2
View File
@@ -22,8 +22,7 @@
> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
> > 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/CrazyLionCat/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br>
官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/CrazyLionCat/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br>
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://github.com/imdap/ruoyi-plus-vben5)<br> > 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://github.com/imdap/ruoyi-plus-vben5)<br>
> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)<br> > 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)<br>
> 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)<br> > 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)<br>
+10 -10
View File
@@ -10,34 +10,34 @@
<name>RuoYi-Vue-Plus</name> <name>RuoYi-Vue-Plus</name>
<url>https://gitee.com/dromara/RuoYi-Vue-Plus</url> <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
<description>Dromara RuoYi-Vue-Plus管理系统</description> <description>Dromara RuoYi-Vue-Plus后台管理系统</description>
<properties> <properties>
<revision>5.5.3</revision> <revision>5.5.3</revision>
<spring-boot.version>4.0.6</spring-boot.version> <spring-boot.version>4.1.0</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>21</java.version> <java.version>21</java.version>
<mybatis.version>3.5.19</mybatis.version> <mybatis.version>3.5.19</mybatis.version>
<springdoc.version>3.0.2</springdoc.version> <springdoc.version>3.0.3</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version> <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<fesod.version>2.0.1-incubating</fesod.version> <fesod.version>2.0.2-incubating</fesod.version>
<velocity.version>2.4.1</velocity.version> <velocity.version>2.4.1</velocity.version>
<satoken.version>1.45.0</satoken.version> <satoken.version>1.45.0</satoken.version>
<mybatis-plus.version>3.5.16</mybatis-plus.version> <mybatis-plus.version>3.5.16</mybatis-plus.version>
<mybatis-plus-join.version>1.5.6</mybatis-plus-join.version> <mybatis-plus-join.version>1.5.6</mybatis-plus-join.version>
<p6spy.version>3.9.1</p6spy.version> <p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.44</hutool.version> <hutool.version>5.8.46</hutool.version>
<spring-boot-admin.version>4.0.4</spring-boot-admin.version> <spring-boot-admin.version>4.0.4</spring-boot-admin.version>
<redisson.version>4.4.0</redisson.version> <redisson.version>4.5.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version> <lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.5.0</dynamic-ds.version> <dynamic-ds.version>4.5.0</dynamic-ds.version>
<snailjob.version>2.0.0</snailjob.version> <snailjob.version>2.0.0</snailjob.version>
<snailai.version>0.0.3</snailai.version> <snailai.version>0.0.4</snailai.version>
<mapstruct-plus.version>1.5.0</mapstruct-plus.version> <mapstruct-plus.version>1.5.0</mapstruct-plus.version>
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version> <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<lombok.version>1.18.42</lombok.version> <lombok.version>1.18.42</lombok.version>
<bouncycastle.version>1.83</bouncycastle.version> <bouncycastle.version>1.84</bouncycastle.version>
<justauth.version>1.16.7</justauth.version> <justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 --> <!-- 离线IP地址定位库 -->
<ip2region.version>3.3.6</ip2region.version> <ip2region.version>3.3.6</ip2region.version>
@@ -52,7 +52,7 @@
<!-- 工作流配置 --> <!-- 工作流配置 -->
<warm-flow.version>1.8.7</warm-flow.version> <warm-flow.version>1.8.7</warm-flow.version>
<!-- mqtt客户端 --> <!-- mqtt客户端 -->
<mica-mqtt.version>2.5.12</mica-mqtt.version> <mica-mqtt.version>2.6.5</mica-mqtt.version>
<easy-es.version>3.0.2</easy-es.version> <easy-es.version>3.0.2</easy-es.version>
<elasticsearch-client.version>7.17.28</elasticsearch-client.version> <elasticsearch-client.version>7.17.28</elasticsearch-client.version>
<!-- Spring AI 2.0 预览版,正式版发布后仅需调整此版本号 --> <!-- Spring AI 2.0 预览版,正式版发布后仅需调整此版本号 -->
@@ -66,7 +66,7 @@
<!-- 统一版本号管理:Maven3.X需要,Maven4.0之后已原生支持 --> <!-- 统一版本号管理:Maven3.X需要,Maven4.0之后已原生支持 -->
<flatten-maven-plugin.version>1.7.3</flatten-maven-plugin.version> <flatten-maven-plugin.version>1.7.3</flatten-maven-plugin.version>
<!-- 打包默认跳过测试 --> <!-- 打包默认跳过测试 -->
<skipTests>true</skipTests> <maven.test.skip>true</maven.test.skip>
</properties> </properties>
<profiles> <profiles>
+6 -4
View File
@@ -11,18 +11,20 @@ RUN mkdir -p /ruoyi/server/logs \
WORKDIR /ruoyi/server WORKDIR /ruoyi/server
ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" ENV SERVER_PORT=8080 SNAIL_JOB_PORT=28080 SNAIL_AI_PORT=38080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE ${SERVER_PORT} EXPOSE ${SERVER_PORT}
# 暴露 snail job 客户端端口 用于定时任务调度中心通信 # 暴露 snail 客户端端口 用于定时任务调度中心通信
EXPOSE ${SNAIL_PORT} EXPOSE ${SNAIL_JOB_PORT}
EXPOSE ${SNAIL_AI_PORT}
ADD ./target/ruoyi-admin.jar ./app.jar ADD ./target/ruoyi-admin.jar ./app.jar
SHELL ["/bin/bash", "-c"] SHELL ["/bin/bash", "-c"]
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
-Dsnail-job.port=${SNAIL_PORT} \ -Dsnail-job.port=${SNAIL_JOB_PORT} \
-Dsnail-ai.port=${SNAIL_AI_PORT} \
# 应用名称 如果想区分集群节点监控 改成不同的名称即可 # 应用名称 如果想区分集群节点监控 改成不同的名称即可
#-Dskywalking.agent.service_name=ruoyi-server \ #-Dskywalking.agent.service_name=ruoyi-server \
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \ #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
-24
View File
@@ -17,30 +17,6 @@
<dependencies> <dependencies>
<!-- ruoyi-admin 同时引入 SnailJob Client 与 SnailAI AgentSnailAI Agent 0.0.3 需要 protobuf 4.x -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>4.35.0</version>
</dependency>
<!-- 局部提升 ruoyi-admin 的 gRPC 运行时,避免被 SnailJob Client 的 1.76.0 抢先选中 -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.81.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.81.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.81.0</version>
</dependency>
<!-- Mysql驱动包 --> <!-- Mysql驱动包 -->
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>com.mysql</groupId>
@@ -58,7 +58,7 @@ public class CaptchaController {
* @param phoneNumber 用户手机号 * @param phoneNumber 用户手机号
* @return 操作结果 * @return 操作结果
*/ */
@RateLimiter(key = "#phonenumber", time = 60, count = 1) @RateLimiter(key = "#phoneNumber", time = 60, count = 1)
@GetMapping("/resource/sms/code") @GetMapping("/resource/sms/code")
public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phoneNumber) { public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phoneNumber) {
if (!RegexValidator.isMobile(phoneNumber)) { if (!RegexValidator.isMobile(phoneNumber)) {
@@ -66,7 +66,6 @@ public class CaptchaController {
} }
String key = GlobalConstants.CAPTCHA_CODE_KEY + phoneNumber; String key = GlobalConstants.CAPTCHA_CODE_KEY + phoneNumber;
String code = RandomUtil.randomNumbers(4); String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
// 验证码模板id 自行处理 (查数据库或写死均可) // 验证码模板id 自行处理 (查数据库或写死均可)
String templateId = ""; String templateId = "";
LinkedHashMap<String, String> map = new LinkedHashMap<>(1); LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
@@ -75,8 +74,10 @@ public class CaptchaController {
SmsResponse smsResponse = smsBlend.sendMessage(phoneNumber, templateId, map); SmsResponse smsResponse = smsBlend.sendMessage(phoneNumber, templateId, map);
if (!smsResponse.isSuccess()) { if (!smsResponse.isSuccess()) {
log.error("验证码短信发送异常 => {}", smsResponse); log.error("验证码短信发送异常 => {}", smsResponse);
return R.fail(smsResponse.getData().toString()); Object data = smsResponse.getData();
return R.fail(data == null ? "验证码短信发送失败" : data.toString());
} }
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
return R.ok(); return R.ok();
} }
@@ -107,13 +108,13 @@ public class CaptchaController {
public void emailCodeImpl(String email) { public void emailCodeImpl(String email) {
String key = GlobalConstants.CAPTCHA_CODE_KEY + email; String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
String code = RandomUtil.randomNumbers(4); String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
try { try {
MailBuilder.of() MailBuilder.of()
.to(email) .to(email)
.subject("登录验证码") .subject("登录验证码")
.text("您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。") .text("您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。")
.send(); .send();
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
} catch (Exception e) { } catch (Exception e) {
log.error("验证码短信发送异常 => {}", e.getMessage()); log.error("验证码短信发送异常 => {}", e.getMessage());
throw new ServiceException(e.getMessage()); throw new ServiceException(e.getMessage());
@@ -97,7 +97,7 @@ public class SysLoginService {
sysSocialService.insertByBo(bo); sysSocialService.insertByBo(bo);
} else { } else {
// 更新用户信息 // 更新用户信息
bo.setId(list.get(0).getId()); bo.setId(list.getFirst().getId());
sysSocialService.updateByBo(bo); sysSocialService.updateByBo(bo);
// 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断 // 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断
// throw new ServiceException("此平台账号已经被绑定!"); // throw new ServiceException("此平台账号已经被绑定!");
@@ -68,7 +68,7 @@ public class SocialAuthStrategy implements IAuthStrategy {
if (CollUtil.isEmpty(list)) { if (CollUtil.isEmpty(list)) {
throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!"); throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
} }
SysUserVo user = loadUser(list.get(0).getUserId()); SysUserVo user = loadUser(list.getFirst().getUserId());
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
LoginUser loginUser = loginService.buildLoginUser(user); LoginUser loginUser = loginService.buildLoginUser(user);
loginUser.setClientKey(client.getClientKey()); loginUser.setClientKey(client.getClientKey());
@@ -137,7 +137,7 @@ spring.data:
# 数据库索引 # 数据库索引
database: 0 database: 0
# redis 密码必须配置 # redis 密码必须配置
password: ruoyi123 # password: ruoyi123
# 连接超时时间 # 连接超时时间
timeout: 10s timeout: 10s
# 是否开启ssl # 是否开启ssl
@@ -161,7 +161,7 @@ springdoc:
enabled: true enabled: true
info: info:
# 标题 # 标题
title: '标题:RuoYi-Vue-Plus管理系统_接口文档' title: '标题:RuoYi-Vue-Plus后台管理系统_接口文档'
# 描述 # 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本 # 版本
@@ -191,6 +191,7 @@ xss:
# 排除链接 # 排除链接
excludeUrls: excludeUrls:
- /system/notice - /system/notice
- /warm-flow/save-json
--- # 分布式锁 lock4j 全局配置 --- # 分布式锁 lock4j 全局配置
lock4j: lock4j:
@@ -262,8 +263,8 @@ mqtt.client:
timeout: 5 timeout: 5
# 重连时间,默认 5000 毫秒 # 重连时间,默认 5000 毫秒
re-interval: 5000 re-interval: 5000
# mqtt 协议版本,可选 MQTT_3_1、mqtt_3_1_1、mqtt_5,默认:mqtt_3_1_1 # mqtt 协议版本,可选 MQTT_3_1、mqtt_3_1_1、mqtt_5,默认:mqtt_5
version: mqtt_3_1_1 version: mqtt_5
# 接收数据的 buffer size,默认:8k # 接收数据的 buffer size,默认:8k
read-buffer-size: 8KB read-buffer-size: 8KB
# 消息解析最大 bytes 长度,默认:10M # 消息解析最大 bytes 长度,默认:10M
@@ -98,9 +98,8 @@ public class ThreadPoolConfig {
* @param t 任务执行过程中抛出的异常 * @param t 任务执行过程中抛出的异常
*/ */
public static void printException(Runnable r, Throwable t) { public static void printException(Runnable r, Throwable t) {
if (t == null && r instanceof Future<?>) { if (t == null && r instanceof Future<?> future) {
try { try {
Future<?> future = (Future<?>) r;
if (future.isDone()) { if (future.isDone()) {
future.get(); future.get();
} }
@@ -1,152 +0,0 @@
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.utils.StringUtils;
/*
* 日期格式
* "yyyy"4位数的年份,例如:2023年表示为"2023"。
* "yy"2位数的年份,例如:2023年表示为"23"。
* "MM"2位数的月份,取值范围为01到12,例如:7月表示为"07"。
* "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。
* "dd"2位数的日期,取值范围为01到31,例如:22日表示为"22"。
* "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。
* "EEEE":星期的全名,例如:星期三表示为"Wednesday"。
* "E":星期的缩写,例如:星期三表示为"Wed"。
* "DDD" 或 "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。
* 时间格式
* "HH"24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。
* "hh"12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。
* "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。
* "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。
* "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。
*/
/**
* 日期格式与时间格式枚举
*/
@Getter
@AllArgsConstructor
public enum FormatsType {
/**
* 例如:2023年表示为"23"
*/
YY("yy"),
/**
* 例如:2023年表示为"2023"
*/
YYYY("yyyy"),
/**
* 例例如,2023年7月可以表示为 "2023-07"
*/
YYYY_MM("yyyy-MM"),
/**
* 例如,日期 "2023年7月22日" 可以表示为 "2023-07-22"
*/
YYYY_MM_DD("yyyy-MM-dd"),
/**
* 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023-07-22 15:30"
*/
YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"),
/**
* 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45"
*/
YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"),
/**
* 例如:下午3点30分45秒,表示为 "15:30:45"
*/
HH_MM_SS("HH:mm:ss"),
/**
* 例例如,2023年7月可以表示为 "2023/07"
*/
YYYY_MM_SLASH("yyyy/MM"),
/**
* 例如,日期 "2023年7月22日" 可以表示为 "2023/07/22"
*/
YYYY_MM_DD_SLASH("yyyy/MM/dd"),
/**
* 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
*/
YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"),
/**
* 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
*/
YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"),
/**
* 例例如,2023年7月可以表示为 "2023.07"
*/
YYYY_MM_DOT("yyyy.MM"),
/**
* 例如,日期 "2023年7月22日" 可以表示为 "2023.07.22"
*/
YYYY_MM_DD_DOT("yyyy.MM.dd"),
/**
* 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023.07.22 15:30"
*/
YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"),
/**
* 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45"
*/
YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"),
/**
* 例如,2023年7月可以表示为 "202307"
*/
YYYYMM("yyyyMM"),
/**
* 例如,2023年7月22日可以表示为 "20230722"
*/
YYYYMMDD("yyyyMMdd"),
/**
* 例如,2023年7月22日下午3点可以表示为 "2023072215"
*/
YYYYMMDDHH("yyyyMMddHH"),
/**
* 例如,2023年7月22日下午3点30分可以表示为 "202307221530"
*/
YYYYMMDDHHMM("yyyyMMddHHmm"),
/**
* 例如,2023年7月22日下午3点30分45秒可以表示为 "20230722153045"
*/
YYYYMMDDHHMMSS("yyyyMMddHHmmss");
/**
* 时间格式
*/
private final String timeFormat;
/**
* 根据字符串内容匹配时间格式类型。
*
* @param str 字符串内容
* @return 时间格式类型
*/
public static FormatsType getFormatsType(String str) {
for (FormatsType value : values()) {
if (StringUtils.contains(str, value.getTimeFormat())) {
return value;
}
}
throw new RuntimeException("'FormatsType' not found By " + str);
}
}
@@ -1,11 +1,11 @@
package org.dromara.common.core.exception.base; package org.dromara.common.core.exception.base;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils;
import java.io.Serial; import java.io.Serial;
@@ -1,347 +1,96 @@
package org.dromara.common.core.utils; package org.dromara.common.core.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import org.apache.commons.lang3.time.DateFormatUtils; import cn.hutool.core.lang.Assert;
import org.dromara.common.core.enums.FormatsType; import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date; import java.util.Date;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* 时间工具类 * 时间工具类
* *
* @author ruoyi * @author AprilWind
*/ */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils { @NoArgsConstructor(access = AccessLevel.PRIVATE)
/** public class DateUtils extends DateUtil {
* 日期解析格式集合。
*/
private static final String[] PARSE_PATTERNS = {
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
/** /**
* 工具类不允许实例化。 * 计算时间差并格式化(精确到秒)
*/
@Deprecated
private DateUtils() {
}
/**
* 获取当前日期和时间
* *
* @return 当前日期和时间的 Date 对象表示 * @param start 开始时间(支持 Date/LocalDateTime)
* @param end 结束时间(支持 Date/LocalDateTime)
* @return 时分秒格式时间差
*/ */
public static Date getNowDate() { public static String formatBetweenBySecond(Object start, Object end) {
return new Date(); return formatTimeBetween(start, end, BetweenFormatter.Level.SECOND);
} }
/** /**
* 获取当前日期的字符串表示,格式为YYYY-MM-DD * 通用时间差格式化
*
* @return 当前日期的字符串表示
*/
public static String getDate() {
return dateTimeNow(FormatsType.YYYY_MM_DD);
}
/**
* 获取当前日期的字符串表示,格式为yyyyMMdd
*
* @return 当前日期的字符串表示
*/
public static String getCurrentDate() {
return DateFormatUtils.format(new Date(), FormatsType.YYYYMMDD.getTimeFormat());
}
/**
* 获取当前日期的路径格式字符串,格式为"yyyy/MM/dd"
*
* @return 当前日期的路径格式字符串
*/
public static String datePath() {
Date now = new Date();
return DateFormatUtils.format(now, FormatsType.YYYY_MM_DD_SLASH.getTimeFormat());
}
/**
* 获取当前时间的字符串表示,格式为YYYY-MM-DD HH:MM:SS
*
* @return 当前时间的字符串表示
*/
public static String getTime() {
return dateTimeNow(FormatsType.YYYY_MM_DD_HH_MM_SS);
}
/**
* 获取当前时间的字符串表示,格式为 "HH:MM:SS"
*
* @return 当前时间的字符串表示,格式为 "HH:MM:SS"
*/
public static String getTimeWithHourMinuteSecond() {
return dateTimeNow(FormatsType.HH_MM_SS);
}
/**
* 获取当前日期和时间的字符串表示,格式为YYYYMMDDHHMMSS
*
* @return 当前日期和时间的字符串表示
*/
public static String dateTimeNow() {
return dateTimeNow(FormatsType.YYYYMMDDHHMMSS);
}
/**
* 获取当前日期和时间的指定格式的字符串表示
*
* @param format 日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
* @return 当前日期和时间的字符串表示
*/
public static String dateTimeNow(final FormatsType format) {
return parseDateToStr(format, new Date());
}
/**
* 将指定日期格式化为 YYYY-MM-DD 格式的字符串
*
* @param date 要格式化的日期对象
* @return 格式化后的日期字符串
*/
public static String formatDate(final Date date) {
return parseDateToStr(FormatsType.YYYY_MM_DD, date);
}
/**
* 将指定日期格式化为 YYYY-MM-DD HH:MM:SS 格式的字符串
*
* @param date 要格式化的日期对象
* @return 格式化后的日期时间字符串
*/
public static String formatDateTime(final Date date) {
return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, date);
}
/**
* 将 LocalDateTime 格式化为 YYYY-MM-DD HH:MM:SS 格式的字符串
*
* @param dateTime 要格式化的 LocalDateTime 对象
* @return 格式化后的日期时间字符串
*/
public static String formatDateTime(final LocalDateTime dateTime) {
return dateTime.format(DateTimeFormatter.ofPattern(FormatsType.YYYY_MM_DD_HH_MM_SS.getTimeFormat()));
}
/**
* 将指定日期按照指定格式进行格式化
*
* @param format 要使用的日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
* @param date 要格式化的日期对象
* @return 格式化后的日期时间字符串
*/
public static String parseDateToStr(final FormatsType format, final Date date) {
return new SimpleDateFormat(format.getTimeFormat()).format(date);
}
/**
* 将指定格式的日期时间字符串转换为 Date 对象
*
* @param format 要解析的日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
* @param ts 要解析的日期时间字符串
* @return 解析后的 Date 对象
* @throws RuntimeException 如果解析过程中发生异常
*/
public static Date parseDateTime(final FormatsType format, final String ts) {
try {
return new SimpleDateFormat(format.getTimeFormat()).parse(ts);
} catch (ParseException e) {
throw new ServiceException("日期时间解析失败:" + ts, e);
}
}
/**
* 将对象转换为日期对象
*
* @param str 要转换的对象,通常是字符串
* @return 转换后的日期对象,如果转换失败或输入为null,则返回null
*/
public static Date parseDate(Object str) {
if (str == null) {
return null;
}
try {
return parseDate(str.toString(), PARSE_PATTERNS);
} catch (ParseException e) {
return null;
}
}
/**
* 获取服务器启动时间
*
* @return 服务器启动时间的 Date 对象表示
*/
public static Date getServerStartDate() {
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
return new Date(time);
}
/**
* 计算两个时间之间的时间差,并以指定单位返回(绝对值)
*
* @param start 起始时间
* @param end 结束时间
* @param unit 所需返回的时间单位(DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS
* @return 时间差的绝对值,以指定单位表示
*/
public static long difference(Date start, Date end, TimeUnit unit) {
// 计算时间差,单位为毫秒,取绝对值避免负数
long diffInMillis = Math.abs(end.getTime() - start.getTime());
// 根据目标单位转换时间差
return switch (unit) {
case DAYS -> diffInMillis / TimeUnit.DAYS.toMillis(1);
case HOURS -> diffInMillis / TimeUnit.HOURS.toMillis(1);
case MINUTES -> diffInMillis / TimeUnit.MINUTES.toMillis(1);
case SECONDS -> diffInMillis / TimeUnit.SECONDS.toMillis(1);
case MILLISECONDS -> diffInMillis;
case MICROSECONDS -> TimeUnit.MILLISECONDS.toMicros(diffInMillis);
case NANOSECONDS -> TimeUnit.MILLISECONDS.toNanos(diffInMillis);
};
}
/**
* 计算两个日期之间的时间差,并以天、小时和分钟的格式返回
*
* @param endDate 结束日期
* @param nowDate 当前日期
* @return 表示时间差的字符串,格式为"天 小时 分钟"
*/
public static String getDatePoor(Date endDate, Date nowDate) {
long diffInMillis = endDate.getTime() - nowDate.getTime();
long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
return String.format("%d天 %d小时 %d分钟", day, hour, min);
}
/**
* 计算两个时间点的差值(天、小时、分钟、秒),当值为0时不显示该单位
* *
* @param startDate 开始时间
* @param endDate 结束时间 * @param endDate 结束时间
* @param nowDate 当前时间 * @param level 精度级别
* @return 时间差字符串,格式为 "x天 x小时 x分钟 x秒",若为 0 则不显示 * @return 格式化时长
*/ */
public static String getTimeDifference(Date endDate, Date nowDate) { public static String formatTimeBetween(Object startDate, Object endDate, BetweenFormatter.Level level) {
long diffInMillis = endDate.getTime() - nowDate.getTime(); // 非空校验
long day = TimeUnit.MILLISECONDS.toDays(diffInMillis); Assert.notNull(startDate, "开始时间不能为空");
long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24; Assert.notNull(endDate, "结束时间不能为空");
long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60; Assert.notNull(level, "时间精度级别不能为空");
long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60;
// 构建时间差字符串,条件是值不为0才显示 // 统一转为Date并校验格式合法性
StringBuilder result = new StringBuilder(); Date start = Convert.toDate(startDate);
if (day > 0) { Date end = Convert.toDate(endDate);
result.append(String.format("%d天 ", day)); Assert.notNull(start, "开始时间格式错误,无法解析为时间");
} Assert.notNull(end, "结束时间格式错误,无法解析为时间");
if (hour > 0) {
result.append(String.format("%d小时 ", hour)); long diffMillis = Math.abs(end.getTime() - start.getTime());
} return formatBetween(diffMillis, level);
if (min > 0) {
result.append(String.format("%d分钟 ", min));
}
if (sec > 0) {
result.append(String.format("%d秒", sec));
}
return !result.isEmpty() ? result.toString().trim() : "0秒";
} }
/** /**
* 计算两个 LocalDateTime 时间点的差值(天、小时、分钟、秒),当值为0时不显示该单位 * 校验日期范围及最大时间跨度
*
* @param endDate 结束时间
* @param nowDate 当前时间
* @return 时间差字符串,格式为 "x天 x小时 x分钟 x秒",若为 0 则不显示
*/
public static String getTimeDifference(LocalDateTime endDate, LocalDateTime nowDate) {
long diffInMillis = java.time.Duration.between(nowDate, endDate).toMillis();
long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60;
StringBuilder result = new StringBuilder();
if (day > 0) {
result.append(String.format("%d天 ", day));
}
if (hour > 0) {
result.append(String.format("%d小时 ", hour));
}
if (min > 0) {
result.append(String.format("%d分钟 ", min));
}
if (sec > 0) {
result.append(String.format("%d秒", sec));
}
return !result.isEmpty() ? result.toString().trim() : "0秒";
}
/**
* 将 LocalDateTime 对象转换为 Date 对象
*
* @param temporalAccessor 要转换的 LocalDateTime 对象
* @return 转换后的 Date 对象
*/
public static Date toDate(LocalDateTime temporalAccessor) {
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
/**
* 将 LocalDate 对象转换为 Date 对象
*
* @param temporalAccessor 要转换的 LocalDate 对象
* @return 转换后的 Date 对象
*/
public static Date toDate(LocalDate temporalAccessor) {
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
/**
* 校验日期范围
* *
* @param startDate 开始日期 * @param startDate 开始日期
* @param endDate 结束日期 * @param endDate 结束日期
* @param maxValue 最大时间跨度限制值 * @param maxValue 最大时间跨度限制值
* @param unit 时间跨度的单位,可选择 "DAYS"、"HOURS" 或 "MINUTES" * @param unit 时间单位
*/ */
public static void validateDateRange(Date startDate, Date endDate, int maxValue, TimeUnit unit) { public static void validateDateRange(Object startDate, Object endDate, int maxValue, TimeUnit unit) {
// 校验结束日期不能早于开始日期 // 基础非空校验
if (endDate.before(startDate)) { Assert.notNull(startDate, "开始日期不能为空");
Assert.notNull(endDate, "结束日期不能为空");
Assert.notNull(unit, "时间单位不能为空");
// 统一转换并校验时间合法性
Date start = Convert.toDate(startDate);
Date end = Convert.toDate(endDate);
Assert.notNull(start, "开始日期格式错误,无法解析为时间");
Assert.notNull(end, "结束日期格式错误,无法解析为时间");
// 校验结束时间不能早于开始时间
if (end.before(start)) {
throw new ServiceException("结束日期不能早于开始日期"); throw new ServiceException("结束日期不能早于开始日期");
} }
// 计算时间跨度 long diffMillis = end.getTime() - start.getTime();
long diffInMillis = endDate.getTime() - startDate.getTime();
// 根据单位转换时间跨度
long diff = switch (unit) { long diff = switch (unit) {
case DAYS -> TimeUnit.MILLISECONDS.toDays(diffInMillis); case DAYS -> TimeUnit.MILLISECONDS.toDays(diffMillis);
case HOURS -> TimeUnit.MILLISECONDS.toHours(diffInMillis); case HOURS -> TimeUnit.MILLISECONDS.toHours(diffMillis);
case MINUTES -> TimeUnit.MILLISECONDS.toMinutes(diffInMillis); case MINUTES -> TimeUnit.MILLISECONDS.toMinutes(diffMillis);
default -> throw new IllegalArgumentException("不支持的时间单位"); default -> throw new IllegalArgumentException("不支持的时间单位" + unit.name());
}; };
// 校验时间跨度不超过最大限制
if (diff > maxValue) { if (diff > maxValue) {
throw new ServiceException("最大时间跨度为 {} {}", maxValue, unit.toString().toLowerCase()); String msg = String.format("最大时间跨度为 %d %s", maxValue, unit.name().toLowerCase());
throw new ServiceException(msg);
} }
} }
@@ -352,7 +101,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
* @return 时间段描述 * @return 时间段描述
*/ */
public static String getTodayHour(Date date) { public static String getTodayHour(Date date) {
int hour = DateUtil.hour(date, true); int hour = hour(date, true);
if (hour <= 6) { if (hour <= 6) {
return "凌晨"; return "凌晨";
} else if (hour < 12) { } else if (hour < 12) {
@@ -387,38 +136,37 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
if (date == null) { if (date == null) {
return ""; return "";
} }
Date now = DateUtil.date(); Date now = new Date();
// 未来时间或非今年 // 未来时间或非今年
if (date.after(now) || DateUtil.year(date) != DateUtil.year(now)) { if (date.after(now) || year(date) != year(now)) {
return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM, date); return formatDateTime(now);
} }
// 今天 // 今天
if (DateUtil.isSameDay(date, now)) { if (isSameDay(date, now)) {
long minutes = DateUtil.between(date, now, DateUnit.MINUTE); long minutes = between(date, now, DateUnit.MINUTE);
if (minutes < 1) { if (minutes < 1) {
return "刚刚"; return "刚刚";
} }
if (minutes < 60) { if (minutes < 60) {
return minutes + "分钟前"; return minutes + "分钟前";
} }
return getTodayHour(date) + " " + DateUtil.format(date, "HH:mm"); return getTodayHour(date) + " " + format(date, "HH:mm");
} }
// 昨天 // 昨天
if (DateUtil.isSameDay(date, DateUtil.yesterday())) { if (isSameDay(date, yesterday())) {
return "昨天 " + DateUtil.format(date, "HH:mm"); return "昨天 " + format(date, "HH:mm");
} }
// 本周 // 本周
if (DateUtil.isSameWeek(date, now, true)) { if (isSameWeek(date, now, true)) {
return DateUtil.dayOfWeekEnum(date).toChinese("") return dayOfWeekEnum(date).toChinese("") + " " + format(date, "HH:mm");
+ " " + DateUtil.format(date, "HH:mm");
} }
// 今年内其它时间 // 今年内其它时间
return DateUtil.format(date, "MM-dd HH:mm"); return format(date, "MM-dd HH:mm");
} }
} }
@@ -14,7 +14,6 @@ import java.util.Map;
* Mapstruct 工具类 * Mapstruct 工具类
* <p>参考文档:<a href="https://mapstruct.plus/introduction/quick-start.html">mapstruct-plus</a></p> * <p>参考文档:<a href="https://mapstruct.plus/introduction/quick-start.html">mapstruct-plus</a></p>
* *
*
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
@@ -179,6 +179,7 @@ public class StreamUtils {
/** /**
* 获取 map 中的数据作为新 Map 的 value key 不变 * 获取 map 中的数据作为新 Map 的 value key 不变
*
* @param map 需要处理的map * @param map 需要处理的map
* @param take 取值函数 * @param take 取值函数
* @param <K> map中的key类型 * @param <K> map中的key类型
@@ -23,6 +23,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
public static final String SLASH = "/"; public static final String SLASH = "/";
public static final String COLON = ":";
private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher(); private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();
@Deprecated @Deprecated
@@ -363,6 +365,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
return input; return input;
} }
} }
/** /**
* 将可迭代对象中的元素使用逗号拼接成字符串 * 将可迭代对象中的元素使用逗号拼接成字符串
* *
@@ -6,7 +6,10 @@ import lombok.NoArgsConstructor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.*; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
@@ -64,7 +64,7 @@ public class TreeBuildUtils extends TreeUtil {
if (CollUtil.isEmpty(list)) { if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList(); return CollUtil.newArrayList();
} }
K k = ReflectUtils.invokeGetter(list.get(0), "parentId"); K k = ReflectUtils.invokeGetter(list.getFirst(), "parentId");
return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser); return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
} }
@@ -26,6 +26,9 @@ public class ValidatorUtils {
* @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常 * @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常
*/ */
public static <T> void validate(T object, Class<?>... groups) { public static <T> void validate(T object, Class<?>... groups) {
if (object == null) {
throw new RuntimeException("请求参数不能为空");
}
Set<ConstraintViolation<T>> validate = VALID.validate(object, groups); Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
if (!validate.isEmpty()) { if (!validate.isEmpty()) {
throw new ConstraintViolationException("参数校验异常", validate); throw new ConstraintViolationException("参数校验异常", validate);
@@ -1,60 +1,64 @@
package org.dromara.common.core.utils.file; package org.dromara.common.core.utils.file;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/** /**
* 媒体类型工具类 * 媒体类型工具类
* *
* @author ruoyi * @author ruoyi
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MimeTypeUtils { public class MimeTypeUtils {
/** /**
* PNG 图片 MIME 类型 * PNG 图片 MIME 类型
*/ */
public static final String IMAGE_PNG = "image/png"; public static final String IMAGE_PNG = "image/png";
/** /**
* JPG 图片 MIME 类型 * JPG 图片 MIME 类型
*/ */
public static final String IMAGE_JPG = "image/jpg"; public static final String IMAGE_JPG = "image/jpg";
/** /**
* JPEG 图片 MIME 类型 * JPEG 图片 MIME 类型
*/ */
public static final String IMAGE_JPEG = "image/jpeg"; public static final String IMAGE_JPEG = "image/jpeg";
/** /**
* BMP 图片 MIME 类型 * BMP 图片 MIME 类型
*/ */
public static final String IMAGE_BMP = "image/bmp"; public static final String IMAGE_BMP = "image/bmp";
/** /**
* GIF 图片 MIME 类型 * GIF 图片 MIME 类型
*/ */
public static final String IMAGE_GIF = "image/gif"; public static final String IMAGE_GIF = "image/gif";
/** /**
* 图片扩展名集合。 * 图片扩展名数组
*/ */
public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"}; public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
/** /**
* Flash 扩展名集合。 * Flash 文件扩展名数组
*/ */
public static final String[] FLASH_EXTENSION = {"swf", "flv"}; public static final String[] FLASH_EXTENSION = {"swf", "flv"};
/** /**
* 媒体扩展名集合。 * 视频文件扩展名数组
*/
public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
"asf", "rm", "rmvb"};
/**
* 视频扩展名集合。
*/ */
public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"}; public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
/** /**
* 默认允许上传扩展名集合。 * 媒体文件扩展名数组(音频+视频+Flash)
*/
public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", "asf", "rm", "rmvb"};
/**
* 默认允许上传的文件扩展名数组
*/ */
public static final String[] DEFAULT_ALLOWED_EXTENSION = { public static final String[] DEFAULT_ALLOWED_EXTENSION = {
// 图片 // 图片
@@ -66,6 +70,47 @@ public class MimeTypeUtils {
// 视频格式 // 视频格式
"mp4", "avi", "rmvb", "mp4", "avi", "rmvb",
// pdf // pdf
"pdf"}; "pdf"
};
/**
* 判断文件扩展名是否为图片格式(忽略大小写)
*
* @param extension 文件扩展名
* @return 是图片返回 true,否则返回 false
*/
public static boolean isImage(String extension) {
return StrUtil.equalsAnyIgnoreCase(extension, IMAGE_EXTENSION);
}
/**
* 判断文件扩展名是否为视频格式(忽略大小写)
*
* @param extension 文件扩展名
* @return 是视频返回 true,否则返回 false
*/
public static boolean isVideo(String extension) {
return StrUtil.equalsAnyIgnoreCase(extension, VIDEO_EXTENSION);
}
/**
* 判断文件扩展名是否为媒体格式(忽略大小写)
*
* @param extension 文件扩展名
* @return 是媒体返回 true,否则返回 false
*/
public static boolean isMedia(String extension) {
return StrUtil.equalsAnyIgnoreCase(extension, MEDIA_EXTENSION);
}
/**
* 判断文件扩展名是否在默认允许上传范围内(忽略大小写)
*
* @param extension 文件扩展名
* @return 允许返回 true,不允许返回 false
*/
public static boolean isDefaultAllowed(String extension) {
return StrUtil.equalsAnyIgnoreCase(extension, DEFAULT_ALLOWED_EXTENSION);
}
} }
@@ -1,6 +1,8 @@
package org.dromara.common.core.utils.ip; package org.dromara.common.core.utils.ip;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
@@ -19,6 +21,7 @@ import java.time.Duration;
* @author 秋辞未寒 * @author 秋辞未寒
*/ */
@Slf4j @Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RegionUtils { public class RegionUtils {
// 默认IPv4地址库文件路径 // 默认IPv4地址库文件路径
@@ -1,9 +1,9 @@
package org.dromara.common.core.utils.reflect; package org.dromara.common.core.utils.reflect;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import org.dromara.common.core.utils.StringUtils;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.StringUtils;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -12,7 +12,6 @@ import java.lang.reflect.Method;
* *
* @author Lion Li * @author Lion Li
*/ */
@SuppressWarnings("rawtypes")
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ReflectUtils extends ReflectUtil { public class ReflectUtils extends ReflectUtil {
@@ -2,6 +2,8 @@ package org.dromara.common.core.utils.regex;
import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.ReUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.constant.RegexConstants; import org.dromara.common.core.constant.RegexConstants;
/** /**
@@ -9,6 +11,7 @@ import org.dromara.common.core.constant.RegexConstants;
* *
* @author AprilWind * @author AprilWind
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class RegexUtils extends ReUtil { public final class RegexUtils extends ReUtil {
/** /**
@@ -2,6 +2,8 @@ package org.dromara.common.core.utils.regex;
import cn.hutool.core.exceptions.ValidateException; import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.lang.Validator; import cn.hutool.core.lang.Validator;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.factory.RegexPatternPoolFactory; import org.dromara.common.core.factory.RegexPatternPoolFactory;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -12,6 +14,7 @@ import java.util.regex.Pattern;
* *
* @author AprilWind * @author AprilWind
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RegexValidator extends Validator { public class RegexValidator extends Validator {
/** /**
@@ -3,7 +3,10 @@ package org.dromara.common.core.validate.enums;
import jakarta.validation.Constraint; import jakarta.validation.Constraint;
import jakarta.validation.Payload; import jakarta.validation.Payload;
import java.lang.annotation.*; import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -2,6 +2,7 @@ package org.dromara.common.core.xss;
import jakarta.validation.Constraint; import jakarta.validation.Constraint;
import jakarta.validation.Payload; import jakarta.validation.Payload;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@@ -6,9 +6,9 @@ import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityRequirement;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.doc.config.properties.SpringDocProperties;
import org.dromara.common.doc.core.customizer.ClassTagOperationCustomizer; import org.dromara.common.doc.core.customizer.ClassTagOperationCustomizer;
import org.dromara.common.doc.core.customizer.JavadocOperationCustomizer; import org.dromara.common.doc.core.customizer.JavadocOperationCustomizer;
import org.dromara.common.doc.config.properties.SpringDocProperties;
import org.dromara.common.doc.core.resolver.JavadocResolver; import org.dromara.common.doc.core.resolver.JavadocResolver;
import org.dromara.common.doc.core.resolver.SaTokenAnnotationMetadataJavadocResolver; import org.dromara.common.doc.core.resolver.SaTokenAnnotationMetadataJavadocResolver;
import org.springdoc.core.configuration.SpringDocConfiguration; import org.springdoc.core.configuration.SpringDocConfiguration;
@@ -56,7 +56,7 @@ public class SpringDocProperties {
* </p> * </p>
* *
* @see io.swagger.v3.oas.models.info.Info * @see io.swagger.v3.oas.models.info.Info
* * <p>
* 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来 * 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来
*/ */
@Data @Data
@@ -18,13 +18,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -82,6 +82,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 执行解析并返回解析到的 Javadoc 内容 * 执行解析并返回解析到的 Javadoc 内容
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param operation Swagger Operation实例 * @param operation Swagger Operation实例
* @param metadata 元信息 * @param metadata 元信息
@@ -91,6 +92,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 检查处理器方法所属的类上是否存在注解 * 检查处理器方法所属的类上是否存在注解
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationClass 注解类 * @param annotationClass 注解类
* @return 是否存在注解 * @return 是否存在注解
@@ -101,6 +103,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 检查处理器方法所属的类上是否存在注解 * 检查处理器方法所属的类上是否存在注解
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationTypeName 注解类名称 * @param annotationTypeName 注解类名称
* @return 是否存在注解 * @return 是否存在注解
@@ -111,6 +114,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 检查处理器方法上是否存在注解 * 检查处理器方法上是否存在注解
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationClass 注解类 * @param annotationClass 注解类
* @return 是否存在注解 * @return 是否存在注解
@@ -121,6 +125,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 检查处理器方法上是否存在注解 * 检查处理器方法上是否存在注解
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationTypeName 注解类名称 * @param annotationTypeName 注解类名称
* @return 是否存在注解 * @return 是否存在注解
@@ -131,6 +136,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 检查处理器方法上是否存在注解 * 检查处理器方法上是否存在注解
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationClass 注解类 * @param annotationClass 注解类
* @return 是否存在注解 * @return 是否存在注解
@@ -141,6 +147,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 检查处理器方法上是否存在注解 * 检查处理器方法上是否存在注解
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationTypeName 注解类名称 * @param annotationTypeName 注解类名称
* @return 是否存在注解 * @return 是否存在注解
@@ -151,6 +158,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 获取处理器方法所属类上的注解的值 * 获取处理器方法所属类上的注解的值
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationClass 注解类 * @param annotationClass 注解类
* @return 注解的值 * @return 注解的值
@@ -161,6 +169,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 获取处理器方法所属类上的注解的值 * 获取处理器方法所属类上的注解的值
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationClassName 注解类名称 * @param annotationClassName 注解类名称
* @return 注解的值 * @return 注解的值
@@ -173,6 +182,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 获取处理器方法上的注解的值 * 获取处理器方法上的注解的值
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationClass 注解类 * @param annotationClass 注解类
* @return 注解的值 * @return 注解的值
@@ -183,6 +193,7 @@ public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResol
/** /**
* 获取处理器方法所属类上的注解的值 * 获取处理器方法所属类上的注解的值
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param annotationClassName 注解类名称 * @param annotationClassName 注解类名称
* @return 注解的值 * @return 注解的值
@@ -1,8 +1,8 @@
package org.dromara.common.encrypt.core; package org.dromara.common.encrypt.core;
import lombok.Data;
import org.dromara.common.encrypt.enums.AlgorithmType; import org.dromara.common.encrypt.enums.AlgorithmType;
import org.dromara.common.encrypt.enums.EncodeType; import org.dromara.common.encrypt.enums.EncodeType;
import lombok.Data;
/** /**
* 加密上下文 用于encryptor传递必要的参数。 * 加密上下文 用于encryptor传递必要的参数。
@@ -21,6 +21,6 @@ public enum EncodeType {
/** /**
* 16进制编码 * 16进制编码
*/ */
HEX; HEX
} }
@@ -5,6 +5,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* api解密属性配置类 * api解密属性配置类
*
* @author wdhcr * @author wdhcr
*/ */
@Data @Data
@@ -1,8 +1,8 @@
package org.dromara.common.encrypt.properties; package org.dromara.common.encrypt.properties;
import lombok.Data;
import org.dromara.common.encrypt.enums.AlgorithmType; import org.dromara.common.encrypt.enums.AlgorithmType;
import org.dromara.common.encrypt.enums.EncodeType; import org.dromara.common.encrypt.enums.EncodeType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
@@ -6,7 +6,7 @@ import java.lang.annotation.*;
/** /**
* excel 列单元格合并(合并列相同项) * excel 列单元格合并(合并列相同项)
* * <p>
* 需搭配 {@link CellMergeStrategy} 策略使用 * 需搭配 {@link CellMergeStrategy} 策略使用
* *
* @author Lion Li * @author Lion Li
@@ -7,6 +7,7 @@ import java.lang.annotation.Target;
/** /**
* 批注 此注解仅用于单表头 不支持多层级表头 * 批注 此注解仅用于单表头 不支持多层级表头
*
* @author guzhouyanyu * @author guzhouyanyu
*/ */
@Target({ElementType.FIELD}) @Target({ElementType.FIELD})
@@ -9,6 +9,7 @@ import java.lang.annotation.Target;
/** /**
* 是否必填 此注解仅用于单表头 不支持多层级表头 * 是否必填 此注解仅用于单表头 不支持多层级表头
*
* @author guzhouyanyu * @author guzhouyanyu
*/ */
@Target({ElementType.FIELD}) @Target({ElementType.FIELD})
@@ -3,6 +3,7 @@ package org.dromara.common.excel.convert;
import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.fesod.sheet.converters.Converter; import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum; import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.apache.fesod.sheet.metadata.GlobalConfiguration; import org.apache.fesod.sheet.metadata.GlobalConfiguration;
@@ -11,7 +12,6 @@ import org.apache.fesod.sheet.metadata.data.WriteCellData;
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty; import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.ExcelEnumFormat; import org.dromara.common.excel.annotation.ExcelEnumFormat;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.HashMap; import java.util.HashMap;
@@ -3,10 +3,10 @@ package org.dromara.common.excel.core;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import org.apache.fesod.sheet.annotation.ExcelIgnore; import org.apache.fesod.sheet.annotation.ExcelIgnore;
import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated; import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated;
import org.apache.fesod.sheet.annotation.ExcelProperty; import org.apache.fesod.sheet.annotation.ExcelProperty;
import lombok.SneakyThrows;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.CellMerge; import org.dromara.common.excel.annotation.CellMerge;
@@ -66,7 +66,7 @@ public class CellMergeHandler {
} }
// 获取有合并注解的字段 // 获取有合并注解的字段
Map<Field, FieldColumnIndex> mergeFields = getFieldColumnIndexMap(rows.get(0).getClass()); Map<Field, FieldColumnIndex> mergeFields = getFieldColumnIndexMap(rows.getFirst().getClass());
// 如果没有需要合并的字段则返回空集 // 如果没有需要合并的字段则返回空集
if (CollUtil.isEmpty(mergeFields)) { if (CollUtil.isEmpty(mergeFields)) {
return Collections.emptyList(); return Collections.emptyList();
@@ -1,6 +1,10 @@
package org.dromara.common.excel.core; package org.dromara.common.excel.core;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fesod.sheet.context.AnalysisContext; import org.apache.fesod.sheet.context.AnalysisContext;
import org.apache.fesod.sheet.event.AnalysisEventListener; import org.apache.fesod.sheet.event.AnalysisEventListener;
import org.apache.fesod.sheet.exception.ExcelAnalysisException; import org.apache.fesod.sheet.exception.ExcelAnalysisException;
@@ -8,10 +12,6 @@ import org.apache.fesod.sheet.exception.ExcelDataConvertException;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -25,27 +25,35 @@ import java.util.stream.Collectors;
@NoArgsConstructor @NoArgsConstructor
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DropDownOptions { public class DropDownOptions {
/** /**
* 一级下拉所在列index,从0开始算 * 一级下拉所在列index,从0开始算
*/ */
private int index = 0; private int index = 0;
/** /**
* 二级下拉所在的index,从0开始算,不能与一级相同 * 二级下拉所在的index,从0开始算,不能与一级相同
*/ */
private int nextIndex = 0; private int nextIndex = 0;
/** /**
* 一级下拉所包含的数据 * 一级下拉所包含的数据
*/ */
private List<String> options = new ArrayList<>(); private List<String> options = new ArrayList<>();
/** /**
* 二级下拉所包含的数据Map * 二级下拉所包含的数据Map
* <p>以每一个一级选项值为Key,每个一级选项对应的二级数据为Value</p> * <p>以每一个一级选项值为Key,每个一级选项对应的二级数据为Value</p>
*/ */
private Map<String, List<String>> nextOptions = new HashMap<>(); private Map<String, List<String>> nextOptions = new HashMap<>();
/** /**
* 分隔符 * 分隔符
*/ */
private static final String DELIMITER = "_"; private static final String DELIMITER = "_";
private static final String OPTION_PART_REGEX = "^[A-Za-z0-9\\u4e00-\\u9fa5]+$";
private static final String EXCEL_NAME_REGEX = "^[A-Za-z_\\u4e00-\\u9fa5][A-Za-z0-9_\\u4e00-\\u9fa5]*$";
private static final String CELL_REFERENCE_REGEX = "^[A-Za-z]{1,3}[1-9][0-9]*$";
/** /**
* 创建只有一级的下拉选 * 创建只有一级的下拉选
@@ -64,10 +72,9 @@ public class DropDownOptions {
*/ */
public static String createOptionValue(Object... vars) { public static String createOptionValue(Object... vars) {
StringBuilder stringBuffer = new StringBuilder(); StringBuilder stringBuffer = new StringBuilder();
String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$";
for (int i = 0; i < vars.length; i++) { for (int i = 0; i < vars.length; i++) {
String var = StrUtil.trimToEmpty(Convert.toStr(vars[i])); String var = StrUtil.trimToEmpty(Convert.toStr(vars[i]));
if (!var.matches(regex)) { if (!var.matches(OPTION_PART_REGEX)) {
throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字"); throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字");
} }
stringBuffer.append(var); stringBuffer.append(var);
@@ -76,10 +83,29 @@ public class DropDownOptions {
stringBuffer.append(DELIMITER); stringBuffer.append(DELIMITER);
} }
} }
if (stringBuffer.toString().matches("^\\d_*$")) { String optionValue = stringBuffer.toString();
validateOptionValue(optionValue);
return optionValue;
}
/**
* 校验级联下拉选项值是否可作为 Excel 名称管理器名称。
*
* @param optionValue 选项值
*/
public static void validateOptionValue(String optionValue) {
if (StrUtil.isBlank(optionValue)) {
throw new ServiceException("选项数据不能为空");
}
if (optionValue.matches("^[0-9].*")) {
throw new ServiceException("禁止以数字开头"); throw new ServiceException("禁止以数字开头");
} }
return stringBuffer.toString(); if (!optionValue.matches(EXCEL_NAME_REGEX)) {
throw new ServiceException("选项数据不符合Excel名称规则,仅允许中英文、数字或下划线,且不能以数字开头");
}
if (optionValue.matches(CELL_REFERENCE_REGEX)) {
throw new ServiceException("选项数据不能为Excel单元格引用");
}
} }
/** /**
@@ -128,7 +154,7 @@ public class DropDownOptions {
sonList.forEach(everySon -> { sonList.forEach(everySon -> {
if (parentGroupByIdMap.containsKey(sonHowToGetParentIdFunction.apply(everySon))) { if (parentGroupByIdMap.containsKey(sonHowToGetParentIdFunction.apply(everySon))) {
// 找到对应的上级 // 找到对应的上级
T parentObj = parentGroupByIdMap.get(sonHowToGetParentIdFunction.apply(everySon)).get(0); T parentObj = parentGroupByIdMap.get(sonHowToGetParentIdFunction.apply(everySon)).getFirst();
// 提取名称和ID作为Key // 提取名称和ID作为Key
String key = howToBuildEveryOption.apply(parentObj); String key = howToBuildEveryOption.apply(parentObj);
// Key对应的Value // Key对应的Value
@@ -202,6 +202,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
if (CollUtil.isEmpty(firstOptions)) { if (CollUtil.isEmpty(firstOptions)) {
return; return;
} }
validateLinkedOptionNames(firstOptions);
Map<String, List<String>> secoundOptionsMap = new HashMap<>(); Map<String, List<String>> secoundOptionsMap = new HashMap<>();
options.getNextOptions().forEach((k, v) -> secoundOptionsMap.put(k, new ArrayList<>(v))); options.getNextOptions().forEach((k, v) -> secoundOptionsMap.put(k, new ArrayList<>(v)));
@@ -281,10 +282,10 @@ public class ExcelDownHandler implements SheetWriteHandler {
continue; continue;
} }
// 取第一个 // 取第一个
String str = data.get(0); String str = data.getFirst();
rowData.add(str); rowData.add(str);
// 通过移除的方式避免重复 // 通过移除的方式避免重复
data.remove(0); data.removeFirst();
// 设置可以继续 // 设置可以继续
flag = true; flag = true;
} }
@@ -313,6 +314,19 @@ public class ExcelDownHandler implements SheetWriteHandler {
currentLinkedOptionsSheetIndex++; currentLinkedOptionsSheetIndex++;
} }
/**
* 校验级联下拉一级选项可作为 Excel 名称,且不能重复。
*/
private void validateLinkedOptionNames(List<String> firstOptions) {
Set<String> names = new HashSet<>();
for (String firstOption : firstOptions) {
DropDownOptions.validateOptionValue(firstOption);
if (!names.add(firstOption)) {
throw new ServiceException("级联下拉一级选项重复:" + firstOption);
}
}
}
/** /**
* <h2>额外表格形式的普通下拉框</h2> * <h2>额外表格形式的普通下拉框</h2>
* 由于下拉框可选值数量过多,为提升Excel打开效率,使用额外表格形式做下拉 * 由于下拉框可选值数量过多,为提升Excel打开效率,使用额外表格形式做下拉
@@ -25,19 +25,10 @@ import org.apache.fesod.sheet.write.style.row.SimpleRowHeightStyleStrategy;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils; import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.excel.convert.ExcelBigNumberConvert; import org.dromara.common.excel.convert.ExcelBigNumberConvert;
import org.dromara.common.excel.core.CellMergeStrategy; import org.dromara.common.excel.core.*;
import org.dromara.common.excel.core.DefaultExcelListener;
import org.dromara.common.excel.core.DropDownOptions;
import org.dromara.common.excel.core.ExcelDownHandler;
import org.dromara.common.excel.core.ExcelListener;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.excel.handler.DataWriteHandler; import org.dromara.common.excel.handler.DataWriteHandler;
import java.io.ByteArrayOutputStream; import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
@@ -125,8 +125,6 @@ public record ExcelWriterWrapper<T>(ExcelWriter excelWriter) {
return new ExcelWriterWrapper<>(excelWriter); return new ExcelWriterWrapper<>(excelWriter);
} }
// -------------------------------- sheet start
/** /**
* 创建工作表。 * 创建工作表。
* *
@@ -207,10 +205,6 @@ public record ExcelWriterWrapper<T>(ExcelWriter excelWriter) {
return FesodSheet.writerSheet(); return FesodSheet.writerSheet();
} }
// -------------------------------- sheet end
// -------------------------------- table start
/** /**
* 创建表格。 * 创建表格。
* *
@@ -109,17 +109,21 @@ public class JsonValueEnhancer {
* @param visited 已访问对象集合,用于避免循环引用 * @param visited 已访问对象集合,用于避免循环引用
*/ */
private void collectValue(Object value, JsonEnhancementContext context, IdentityHashMap<Object, Boolean> visited) { private void collectValue(Object value, JsonEnhancementContext context, IdentityHashMap<Object, Boolean> visited) {
if (value == null) { switch (value) {
case null -> {
return; return;
} }
if (value instanceof Map<?, ?> map) { case Map<?, ?> map -> {
map.values().forEach(child -> collectValue(child, context, visited)); map.values().forEach(child -> collectValue(child, context, visited));
return; return;
} }
if (value instanceof Iterable<?> iterable) { case Iterable<?> iterable -> {
iterable.forEach(child -> collectValue(child, context, visited)); iterable.forEach(child -> collectValue(child, context, visited));
return; return;
} }
default -> {
}
}
if (value.getClass().isArray()) { if (value.getClass().isArray()) {
int length = Array.getLength(value); int length = Array.getLength(value);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
@@ -13,6 +13,10 @@ import tools.jackson.databind.ser.jdk.NumberSerializer;
@JacksonStdImpl @JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer { public class BigNumberSerializer extends NumberSerializer {
/**
* 提供实例
*/
public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class);
/** /**
* 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来 * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来
*/ */
@@ -22,11 +26,6 @@ public class BigNumberSerializer extends NumberSerializer {
*/ */
private static final long MIN_SAFE_INTEGER = -9007199254740991L; private static final long MIN_SAFE_INTEGER = -9007199254740991L;
/**
* 提供实例
*/
public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class);
/** /**
* 构造大数字序列化器。 * 构造大数字序列化器。
* *
@@ -9,9 +9,9 @@ import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch; import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
@@ -1,8 +1,7 @@
package org.dromara.common.log.event; package org.dromara.common.log.event;
import lombok.Data;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
@@ -1,5 +1,8 @@
package org.dromara.common.mqtt.config; package org.dromara.common.mqtt.config;
import net.dreamlu.mica.net.utils.thread.ThreadUtils;
import net.dreamlu.mica.net.utils.thread.pool.SynThreadPoolExecutor;
import net.dreamlu.mica.net.utils.thread.pool.TioCallerRunsPolicy;
import org.dromara.common.mqtt.listener.MqttClientConnectListener; import org.dromara.common.mqtt.listener.MqttClientConnectListener;
import org.dromara.common.mqtt.listener.MqttClientGlobalMessageListener; import org.dromara.common.mqtt.listener.MqttClientGlobalMessageListener;
import org.dromara.mica.mqtt.core.client.MqttClientCreator; import org.dromara.mica.mqtt.core.client.MqttClientCreator;
@@ -8,9 +11,6 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.task.VirtualThreadTaskExecutor; import org.springframework.core.task.VirtualThreadTaskExecutor;
import org.tio.utils.thread.ThreadUtils;
import org.tio.utils.thread.pool.SynThreadPoolExecutor;
import org.tio.utils.thread.pool.TioCallerRunsPolicy;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
@@ -1,9 +1,9 @@
package org.dromara.common.mqtt.listener; package org.dromara.common.mqtt.listener;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.net.core.ChannelContext;
import org.dromara.mica.mqtt.core.client.IMqttClientConnectListener; import org.dromara.mica.mqtt.core.client.IMqttClientConnectListener;
import org.dromara.mica.mqtt.core.client.MqttClientCreator; import org.dromara.mica.mqtt.core.client.MqttClientCreator;
import org.tio.core.ChannelContext;
/** /**
* 客户端连接状态监听 * 客户端连接状态监听
@@ -1,9 +1,9 @@
package org.dromara.common.mqtt.listener; package org.dromara.common.mqtt.listener;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.net.core.ChannelContext;
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage; import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
import org.dromara.mica.mqtt.core.client.IMqttClientGlobalMessageListener; import org.dromara.mica.mqtt.core.client.IMqttClientGlobalMessageListener;
import org.tio.core.ChannelContext;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@@ -122,6 +122,7 @@ public class LambdaCrudChainWrapper<T, V> extends AbstractLambdaWrapper<T, Lambd
* @param columns 查询字段 * @param columns 查询字段
* @return this * @return this
*/ */
@Override
@SafeVarargs @SafeVarargs
public final LambdaCrudChainWrapper<T, V> select(SFunction<T, ?>... columns) { public final LambdaCrudChainWrapper<T, V> select(SFunction<T, ?>... columns) {
return select(true, CollectionUtils.toList(columns)); return select(true, CollectionUtils.toList(columns));
@@ -134,6 +135,7 @@ public class LambdaCrudChainWrapper<T, V> extends AbstractLambdaWrapper<T, Lambd
* @param columns 查询字段 * @param columns 查询字段
* @return this * @return this
*/ */
@Override
@SafeVarargs @SafeVarargs
public final LambdaCrudChainWrapper<T, V> select(boolean condition, SFunction<T, ?>... columns) { public final LambdaCrudChainWrapper<T, V> select(boolean condition, SFunction<T, ?>... columns) {
return select(condition, CollectionUtils.toList(columns)); return select(condition, CollectionUtils.toList(columns));
@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta; import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.ibatis.reflection.property.PropertyNamer; import org.apache.ibatis.reflection.property.PropertyNamer;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -14,6 +16,7 @@ import java.util.regex.Pattern;
* *
* @author Lion Li * @author Lion Li
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class AggregateSelectUtils { public final class AggregateSelectUtils {
/** /**
@@ -21,12 +24,6 @@ public final class AggregateSelectUtils {
*/ */
private static final Pattern ALIAS_PATTERN = Pattern.compile("[A-Za-z_][A-Za-z0-9_]*"); private static final Pattern ALIAS_PATTERN = Pattern.compile("[A-Za-z_][A-Za-z0-9_]*");
/**
* 工具类不允许实例化。
*/
private AggregateSelectUtils() {
}
/** /**
* 追加查询字段 SQL。 * 追加查询字段 SQL。
* *
@@ -400,6 +400,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> eq(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> eq(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.eq(condition, column, value); wrapper.eq(condition, column, value);
return this; return this;
@@ -425,6 +426,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> ne(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> ne(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.ne(condition, column, value); wrapper.ne(condition, column, value);
return this; return this;
@@ -450,6 +452,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> gt(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> gt(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.gt(condition, column, value); wrapper.gt(condition, column, value);
return this; return this;
@@ -475,6 +478,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> ge(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> ge(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.ge(condition, column, value); wrapper.ge(condition, column, value);
return this; return this;
@@ -500,6 +504,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> lt(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> lt(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.lt(condition, column, value); wrapper.lt(condition, column, value);
return this; return this;
@@ -525,6 +530,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> le(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> le(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.le(condition, column, value); wrapper.le(condition, column, value);
return this; return this;
@@ -550,6 +556,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> like(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> like(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.like(condition, column, value); wrapper.like(condition, column, value);
return this; return this;
@@ -575,6 +582,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> notLike(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> notLike(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.notLike(condition, column, value); wrapper.notLike(condition, column, value);
return this; return this;
@@ -600,6 +608,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> likeLeft(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> likeLeft(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.likeLeft(condition, column, value); wrapper.likeLeft(condition, column, value);
return this; return this;
@@ -625,6 +634,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param value 条件值 * @param value 条件值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> likeRight(boolean condition, SFunction<T, ?> column, Object value) { public LambdaQueryBuilder<T> likeRight(boolean condition, SFunction<T, ?> column, Object value) {
wrapper.likeRight(condition, column, value); wrapper.likeRight(condition, column, value);
return this; return this;
@@ -652,6 +662,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param end 结束值 * @param end 结束值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> between(boolean condition, SFunction<T, ?> column, Object begin, Object end) { public LambdaQueryBuilder<T> between(boolean condition, SFunction<T, ?> column, Object begin, Object end) {
wrapper.between(condition, column, begin, end); wrapper.between(condition, column, begin, end);
return this; return this;
@@ -679,6 +690,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param end 结束值 * @param end 结束值
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> notBetween(boolean condition, SFunction<T, ?> column, Object begin, Object end) { public LambdaQueryBuilder<T> notBetween(boolean condition, SFunction<T, ?> column, Object begin, Object end) {
wrapper.notBetween(condition, column, begin, end); wrapper.notBetween(condition, column, begin, end);
return this; return this;
@@ -718,7 +730,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
public LambdaQueryBuilder<T> allEq(BiPredicate<SFunction<T, ?>, Object> filter, Map<?, ?> params, boolean null2IsNull) { public LambdaQueryBuilder<T> allEq(BiPredicate<SFunction<T, ?>, Object> filter, Map<?, ?> params, boolean null2IsNull) {
wrapper.allEq(true, (BiPredicate) filter, (Map) params, null2IsNull); wrapper.allEq(true, filter, (Map) params, null2IsNull);
return this; return this;
} }
@@ -732,7 +744,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
public LambdaQueryBuilder<T> allEq(boolean condition, BiPredicate<SFunction<T, ?>, Object> filter, Map<?, ?> params, boolean null2IsNull) { public LambdaQueryBuilder<T> allEq(boolean condition, BiPredicate<SFunction<T, ?>, Object> filter, Map<?, ?> params, boolean null2IsNull) {
wrapper.allEq(condition, (BiPredicate) filter, (Map) params, null2IsNull); wrapper.allEq(condition, filter, (Map) params, null2IsNull);
return this; return this;
} }
@@ -802,6 +814,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param values 条件值集合 * @param values 条件值集合
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> in(boolean condition, SFunction<T, ?> column, Collection<?> values) { public LambdaQueryBuilder<T> in(boolean condition, SFunction<T, ?> column, Collection<?> values) {
wrapper.in(condition, column, values); wrapper.in(condition, column, values);
return this; return this;
@@ -827,6 +840,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param values 条件值数组 * @param values 条件值数组
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> in(boolean condition, SFunction<T, ?> column, Object... values) { public LambdaQueryBuilder<T> in(boolean condition, SFunction<T, ?> column, Object... values) {
wrapper.in(condition, column, values); wrapper.in(condition, column, values);
return this; return this;
@@ -852,6 +866,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param values 条件值集合 * @param values 条件值集合
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> notIn(boolean condition, SFunction<T, ?> column, Collection<?> values) { public LambdaQueryBuilder<T> notIn(boolean condition, SFunction<T, ?> column, Collection<?> values) {
wrapper.notIn(condition, column, values); wrapper.notIn(condition, column, values);
return this; return this;
@@ -877,6 +892,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param values 条件值数组 * @param values 条件值数组
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> notIn(boolean condition, SFunction<T, ?> column, Object... values) { public LambdaQueryBuilder<T> notIn(boolean condition, SFunction<T, ?> column, Object... values) {
wrapper.notIn(condition, column, values); wrapper.notIn(condition, column, values);
return this; return this;
@@ -1219,6 +1235,7 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* @param values SQL 片段参数 * @param values SQL 片段参数
* @return 当前查询构造辅助对象 * @return 当前查询构造辅助对象
*/ */
@Override
public LambdaQueryBuilder<T> apply(boolean condition, String applySql, Object... values) { public LambdaQueryBuilder<T> apply(boolean condition, String applySql, Object... values) {
wrapper.apply(condition, applySql, values); wrapper.apply(condition, applySql, values);
return this; return this;
@@ -1368,7 +1385,6 @@ public final class LambdaQueryBuilder<T> implements LambdaQueryCondition<T, Lamb
* *
* @return 聚合查询包装器 * @return 聚合查询包装器
*/ */
@SuppressWarnings("unchecked")
private AggregateLambdaQueryWrapper<T> aggregateWrapper() { private AggregateLambdaQueryWrapper<T> aggregateWrapper() {
return (AggregateLambdaQueryWrapper<T>) wrapper; return (AggregateLambdaQueryWrapper<T>) wrapper;
} }
@@ -2,22 +2,13 @@ package org.dromara.common.mybatis.core.query;
import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache; import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta; import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.apache.ibatis.reflection.property.PropertyNamer; import org.apache.ibatis.reflection.property.PropertyNamer;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -112,7 +112,7 @@ public class PlusDataPermissionHandler {
} }
Object defaultValue = "-1"; Object defaultValue = "-1";
NullSafeStandardEvaluationContext context = new NullSafeStandardEvaluationContext(defaultValue); NullSafeStandardEvaluationContext context = new NullSafeStandardEvaluationContext(defaultValue);
context.addPropertyAccessor(new NullSafePropertyAccessor(context.getPropertyAccessors().get(0), defaultValue)); context.addPropertyAccessor(new NullSafePropertyAccessor(context.getPropertyAccessors().getFirst(), defaultValue));
context.setBeanResolver(beanResolver); context.setBeanResolver(beanResolver);
DataPermissionHelper.getContext().forEach(context::setVariable); DataPermissionHelper.getContext().forEach(context::setVariable);
Set<String> conditions = new HashSet<>(); Set<String> conditions = new HashSet<>();
@@ -35,10 +35,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Duration; import java.time.Duration;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.Collection; import java.util.*;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -203,7 +200,7 @@ public abstract class AbstractOssClientImpl implements OssClient {
.orElse(""); .orElse("");
String mergedPrefix = mergePrefix(defaultPrefix, businessPrefix); String mergedPrefix = mergePrefix(defaultPrefix, businessPrefix);
String suffix = suffix(fileName); String suffix = suffix(fileName);
String datePath = DateUtils.datePath(); String datePath = DateUtils.format(new Date(), "yyyy/MM/dd");
String uuid = IdUtil.fastSimpleUUID(); String uuid = IdUtil.fastSimpleUUID();
String path = mergedPrefix.isEmpty() ? datePath + StringUtils.SLASH + uuid : mergedPrefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid; String path = mergedPrefix.isEmpty() ? datePath + StringUtils.SLASH + uuid : mergedPrefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid;
return path + suffix; return path + suffix;
@@ -5,19 +5,20 @@ package org.dromara.common.oss.config;
* *
* @param <T> 配置类型 * @param <T> 配置类型
* @param <B> 配置构建器类型 * @param <B> 配置构建器类型
*
* @author 秋辞未寒 * @author 秋辞未寒
*/ */
public interface Config<T, B> { public interface Config<T, B> {
/** /**
* 配置对象拷贝 * 配置对象拷贝
*
* @return 拷贝后的新配置对象 * @return 拷贝后的新配置对象
*/ */
T copy(); T copy();
/** /**
* 转为构建器对象 * 转为构建器对象
*
* @return 构建器对象 * @return 构建器对象
*/ */
B toBuilder(); B toBuilder();
@@ -60,7 +60,7 @@ public record HandleAsyncResult<T>(
* @return 异步处理结果 * @return 异步处理结果
*/ */
public static <T> HandleAsyncResult<T> of(T result, Throwable error) { public static <T> HandleAsyncResult<T> of(T result, Throwable error) {
return new HandleAsyncResult<T>(result, error); return new HandleAsyncResult<>(result, error);
} }
/** /**
@@ -71,7 +71,7 @@ public record HandleAsyncResult<T>(
* @return 异步处理结果 * @return 异步处理结果
*/ */
public static <T> HandleAsyncResult<T> success(T result) { public static <T> HandleAsyncResult<T> success(T result) {
return new HandleAsyncResult<T>(result, null); return new HandleAsyncResult<>(result, null);
} }
/** /**
@@ -82,6 +82,6 @@ public record HandleAsyncResult<T>(
* @return 异步处理结果 * @return 异步处理结果
*/ */
public static <T> HandleAsyncResult<T> failure(Throwable error) { public static <T> HandleAsyncResult<T> failure(Throwable error) {
return new HandleAsyncResult<T>(null, error); return new HandleAsyncResult<>(null, error);
} }
} }
@@ -3,11 +3,7 @@ package org.dromara.common.push.annotation;
import org.dromara.common.push.condition.MessageTransportCondition; import org.dromara.common.push.condition.MessageTransportCondition;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import java.lang.annotation.Documented; import java.lang.annotation.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* 按消息推送传输方式启用组件。 * 按消息推送传输方式启用组件。
@@ -1,10 +1,10 @@
package org.dromara.common.push.config; package org.dromara.common.push.config;
import org.dromara.common.push.annotation.ConditionalOnMessageTransport; import org.dromara.common.push.annotation.ConditionalOnMessageTransport;
import org.dromara.common.push.listener.MessageTopicListener;
import org.dromara.common.push.core.WebSocketSessionManager; import org.dromara.common.push.core.WebSocketSessionManager;
import org.dromara.common.push.handler.PlusWebSocketHandler; import org.dromara.common.push.handler.PlusWebSocketHandler;
import org.dromara.common.push.interceptor.PlusWebSocketInterceptor; import org.dromara.common.push.interceptor.PlusWebSocketInterceptor;
import org.dromara.common.push.listener.MessageTopicListener;
import org.dromara.common.push.properties.MessageProperties; import org.dromara.common.push.properties.MessageProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -112,13 +112,13 @@ public class RateLimiterAspect {
key = expression.getValue(context, String.class); key = expression.getValue(context, String.class);
} }
StringBuilder stringBuffer = new StringBuilder(GlobalConstants.RATE_LIMIT_KEY); StringBuilder stringBuffer = new StringBuilder(GlobalConstants.RATE_LIMIT_KEY);
stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(":"); stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(StringUtils.COLON);
if (rateLimiter.limitType() == LimitType.IP) { if (rateLimiter.limitType() == LimitType.IP) {
// 获取请求ip // 获取请求ip
stringBuffer.append(ServletUtils.getClientIP()).append(":"); stringBuffer.append(ServletUtils.getClientIP()).append(StringUtils.COLON);
} else if (rateLimiter.limitType() == LimitType.CLUSTER) { } else if (rateLimiter.limitType() == LimitType.CLUSTER) {
// 获取客户端实例id // 获取客户端实例id
stringBuffer.append(RedisUtils.getClient().getId()).append(":"); stringBuffer.append(RedisUtils.getClient().getId()).append(StringUtils.COLON);
} }
return stringBuffer.append(key).toString(); return stringBuffer.append(key).toString();
} }
@@ -24,6 +24,7 @@ import org.dromara.common.redis.utils.RedisUtils;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.lang.reflect.Array;
import java.time.Duration; import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
@@ -62,7 +63,7 @@ public class RepeatSubmitAspect {
// 唯一值(没有消息头则使用请求地址) // 唯一值(没有消息头则使用请求地址)
String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName())); String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName()));
submitKey = SecureUtil.md5(submitKey + ":" + nowParams); submitKey = SecureUtil.md5(submitKey + StringUtils.COLON + nowParams);
// 唯一标识(指定key + url + 消息头) // 唯一标识(指定key + url + 消息头)
String cacheRepeatKey = GlobalConstants.REPEAT_SUBMIT_KEY + url + submitKey; String cacheRepeatKey = GlobalConstants.REPEAT_SUBMIT_KEY + url + submitKey;
if (RedisUtils.setObjectIfAbsent(cacheRepeatKey, "", Duration.ofMillis(interval))) { if (RedisUtils.setObjectIfAbsent(cacheRepeatKey, "", Duration.ofMillis(interval))) {
@@ -150,16 +151,30 @@ public class RepeatSubmitAspect {
public boolean isFilterObject(final Object o) { public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass(); Class<?> clazz = o.getClass();
if (clazz.isArray()) { if (clazz.isArray()) {
return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); if (MultipartFile.class.isAssignableFrom(clazz.getComponentType())) {
return true;
}
int length = Array.getLength(o);
for (int i = 0; i < length; i++) {
Object value = Array.get(o, i);
if (ObjectUtil.isNotNull(value) && isFilterObject(value)) {
return true;
}
}
return false;
} else if (Collection.class.isAssignableFrom(clazz)) { } else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) o; Collection collection = (Collection) o;
for (Object value : collection) { for (Object value : collection) {
return value instanceof MultipartFile; if (ObjectUtil.isNotNull(value) && isFilterObject(value)) {
return true;
}
} }
} else if (Map.class.isAssignableFrom(clazz)) { } else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) o; Map map = (Map) o;
for (Object value : map.values()) { for (Object value : map.values()) {
return value instanceof MultipartFile; if (ObjectUtil.isNotNull(value) && isFilterObject(value)) {
return true;
}
} }
} }
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
@@ -24,7 +24,6 @@ import tools.jackson.databind.module.SimpleModule;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
/** /**
@@ -21,7 +21,7 @@ public class KeyPrefixHandler implements NameMapper {
*/ */
public KeyPrefixHandler(String keyPrefix) { public KeyPrefixHandler(String keyPrefix) {
//前缀为空 则返回空前缀 //前缀为空 则返回空前缀
this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":"; this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + StringUtils.COLON;
} }
/** /**
@@ -1,5 +1,6 @@
package org.dromara.common.redis.manager; package org.dromara.common.redis.manager;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@@ -55,7 +56,7 @@ public class CaffeineCacheDecorator implements Cache {
* @return 唯一键 * @return 唯一键
*/ */
public String getUniqueKey(Object key) { public String getUniqueKey(Object key) {
return name + ":" + key; return name + StringUtils.COLON + key;
} }
/** /**
@@ -175,7 +176,7 @@ public class CaffeineCacheDecorator implements Cache {
* 清理当前缓存命名空间下的本地一级缓存 * 清理当前缓存命名空间下的本地一级缓存
*/ */
private void clearLocalCache() { private void clearLocalCache() {
String prefix = name + ":"; String prefix = name + StringUtils.COLON;
caffeine.asMap().keySet().removeIf(key -> key instanceof String cacheKey && cacheKey.startsWith(prefix)); caffeine.asMap().keySet().removeIf(key -> key instanceof String cacheKey && cacheKey.startsWith(prefix));
} }
@@ -22,7 +22,7 @@ import java.util.stream.Stream;
* @version 3.1.0 新增 * @version 3.1.0 新增
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked", "rawtypes"}) @SuppressWarnings(value = {"rawtypes"})
public class RedisUtils { public class RedisUtils {
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class); private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
@@ -280,9 +280,7 @@ public class RedisUtils {
return; return;
} }
RBatch batch = CLIENT.createBatch(); RBatch batch = CLIENT.createBatch();
collection.forEach(t -> { collection.forEach(t -> batch.getBucket(t.toString()).deleteAsync());
batch.getBucket(t.toString()).deleteAsync();
});
batch.execute(); batch.execute();
} }
@@ -561,15 +559,16 @@ public class RedisUtils {
/** /**
* 获得缓存的基本对象列表(全局匹配忽略租户 自行拼接租户id) * 获得缓存的基本对象列表(全局匹配忽略租户 自行拼接租户id)
* <P> * <p>
* limit-设置扫描的限制数量(默认为0,查询全部) * limit-设置扫描的限制数量(默认为0,查询全部)
* pattern-设置键的匹配模式(默认为null) * pattern-设置键的匹配模式(默认为null)
* chunkSize-设置每次扫描的块大小(默认为0,本方法设置为1000) * chunkSize-设置每次扫描的块大小(默认为0,本方法设置为1000)
* type-设置键的类型(默认为null,查询全部类型) * type-设置键的类型(默认为null,查询全部类型)
* </P> * </P>
* @see KeysScanOptions *
* @param pattern 字符串前缀 * @param pattern 字符串前缀
* @return 对象列表 * @return 对象列表
* @see KeysScanOptions
*/ */
public static Collection<String> keys(final String pattern) { public static Collection<String> keys(final String pattern) {
return keys(KeysScanOptions.defaults().pattern(pattern).chunkSize(1000)); return keys(KeysScanOptions.defaults().pattern(pattern).chunkSize(1000));
@@ -579,14 +578,14 @@ public class RedisUtils {
* 通过扫描参数获取缓存的基本对象列表 * 通过扫描参数获取缓存的基本对象列表
* *
* @param keysScanOptions 扫描参数 * @param keysScanOptions 扫描参数
* <P> * <p>
* limit-设置扫描的限制数量(默认为0,查询全部) * limit-设置扫描的限制数量(默认为0,查询全部)
* pattern-设置键的匹配模式(默认为null) * pattern-设置键的匹配模式(默认为null)
* chunkSize-设置每次扫描的块大小(默认为0) * chunkSize-设置每次扫描的块大小(默认为0)
* type-设置键的类型(默认为null,查询全部类型) * type-设置键的类型(默认为null,查询全部类型)
* </P> * </P>
* @see KeysScanOptions
* @return 对象列表 * @return 对象列表
* @see KeysScanOptions
*/ */
public static Collection<String> keys(final KeysScanOptions keysScanOptions) { public static Collection<String> keys(final KeysScanOptions keysScanOptions) {
Stream<String> keysStream = CLIENT.getKeys().getKeysStream(keysScanOptions); Stream<String> keysStream = CLIENT.getKeys().getKeysStream(keysScanOptions);
@@ -4,6 +4,7 @@ import cn.dev33.satoken.dao.auto.SaTokenDaoBySessionFollowObject;
import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaFoxUtil;
import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import java.time.Duration; import java.time.Duration;
@@ -157,7 +158,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject {
@Override @Override
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) { public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
String pattern = prefix + "*" + keyword + "*"; String pattern = prefix + "*" + keyword + "*";
String cacheKey = pattern + start + ":" + size + ":" + sortType; String cacheKey = pattern + start + StringUtils.COLON + size + StringUtils.COLON + sortType;
return (List<String>) CAFFEINE.get(cacheKey, k -> { return (List<String>) CAFFEINE.get(cacheKey, k -> {
Collection<String> keys = RedisUtils.keys(pattern); Collection<String> keys = RedisUtils.keys(pattern);
List<String> list = new ArrayList<>(keys); List<String> list = new ArrayList<>(keys);
@@ -9,8 +9,8 @@ import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.api.model.LoginUser; import org.dromara.system.api.model.LoginUser;
import java.util.Collection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -45,7 +45,14 @@ public class SaTokenExceptionHandler {
public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) { public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage()); log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"); String msg = switch (e.getType()) {
case NotLoginException.TOKEN_TIMEOUT -> "登录已过期,请重新登录";
case NotLoginException.BE_REPLACED -> "当前账号已在其他设备登录,您已被强制下线";
case NotLoginException.KICK_OUT -> "账号已被管理员强制下线";
case NotLoginException.TOKEN_FREEZE -> "账号已被冻结,请联系管理员处理";
default -> "登录状态异常,请重新登录";
};
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, msg);
} }
} }
@@ -1,9 +1,9 @@
package org.dromara.common.satoken.utils; package org.dromara.common.satoken.utils;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgent;
@@ -130,7 +130,7 @@ public class SecurityConfig implements WebMvcConfigurer {
return new SaServletFilter() return new SaServletFilter()
.addInclude("/actuator", "/actuator/**") .addInclude("/actuator", "/actuator/**")
.setAuth(obj -> { .setAuth(obj -> {
SaHttpBasicUtil.check(username + ":" + password); SaHttpBasicUtil.check(username + StringUtils.COLON + password);
}) })
.setError(e -> { .setError(e -> {
HttpServletResponse response = ServletUtils.getResponse(); HttpServletResponse response = ServletUtils.getResponse();
@@ -1,8 +1,8 @@
package org.dromara.common.security.handler; package org.dromara.common.security.handler;
import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.ReUtil;
import org.dromara.common.core.utils.SpringUtils;
import lombok.Data; import lombok.Data;
import org.dromara.common.core.utils.SpringUtils;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
@@ -2,11 +2,7 @@ package org.dromara.common.sensitive.annotation;
import org.dromara.common.sensitive.core.SensitiveStrategy; import org.dromara.common.sensitive.core.SensitiveStrategy;
import java.lang.annotation.Documented; import java.lang.annotation.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* 数据脱敏注解 * 数据脱敏注解
@@ -124,7 +124,6 @@ public class AuthTopIamRequest extends AuthDefaultRequest {
.add("Authorization", "Bearer " + authToken.getAccessToken()), false).getBody(); .add("Authorization", "Bearer " + authToken.getAccessToken()), false).getBody();
} }
/** /**
* 生成 TopIAM 授权地址 * 生成 TopIAM 授权地址
* *
@@ -33,7 +33,6 @@ public class SocialUtils {
* @return 授权响应 * @return 授权响应
* @throws AuthException 授权异常 * @throws AuthException 授权异常
*/ */
@SuppressWarnings("unchecked")
public static AuthResponse<AuthUser> loginAuth(String source, String code, String state, SocialProperties socialProperties) throws AuthException { public static AuthResponse<AuthUser> loginAuth(String source, String code, String state, SocialProperties socialProperties) throws AuthException {
AuthRequest authRequest = getAuthRequest(source, socialProperties); AuthRequest authRequest = getAuthRequest(source, socialProperties);
AuthCallback callback = new AuthCallback(); AuthCallback callback = new AuthCallback();
@@ -69,7 +68,8 @@ public class SocialUtils {
case "coding" -> new AuthCodingRequest(builder.build(), STATE_CACHE); case "coding" -> new AuthCodingRequest(builder.build(), STATE_CACHE);
case "oschina" -> new AuthOschinaRequest(builder.build(), STATE_CACHE); case "oschina" -> new AuthOschinaRequest(builder.build(), STATE_CACHE);
// 支付宝在创建回调地址时不允许使用localhost或者127.0.0.1所以这儿的回调地址使用的局域网内的ip // 支付宝在创建回调地址时不允许使用localhost或者127.0.0.1所以这儿的回调地址使用的局域网内的ip
case "alipay_wallet" -> new AuthAlipayRequest(builder.build(), socialProperties.getType().get("alipay_wallet").getAlipayPublicKey(), STATE_CACHE); case "alipay_wallet" ->
new AuthAlipayRequest(builder.build(), socialProperties.getType().get("alipay_wallet").getAlipayPublicKey(), STATE_CACHE);
case "qq" -> new AuthQqRequest(builder.build(), STATE_CACHE); case "qq" -> new AuthQqRequest(builder.build(), STATE_CACHE);
case "wechat_open" -> new AuthWeChatOpenRequest(builder.build(), STATE_CACHE); case "wechat_open" -> new AuthWeChatOpenRequest(builder.build(), STATE_CACHE);
case "taobao" -> new AuthTaobaoRequest(builder.build(), STATE_CACHE); case "taobao" -> new AuthTaobaoRequest(builder.build(), STATE_CACHE);
@@ -77,9 +77,11 @@ public class SocialUtils {
case "linkedin" -> new AuthLinkedinRequest(builder.build(), STATE_CACHE); case "linkedin" -> new AuthLinkedinRequest(builder.build(), STATE_CACHE);
case "microsoft" -> new AuthMicrosoftRequest(builder.tenantId(obj.getTenantId()).build(), STATE_CACHE); case "microsoft" -> new AuthMicrosoftRequest(builder.tenantId(obj.getTenantId()).build(), STATE_CACHE);
case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE); case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE);
case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey(obj.getStackOverflowKey()).build(), STATE_CACHE); case "stack_overflow" ->
new AuthStackOverflowRequest(builder.stackOverflowKey(obj.getStackOverflowKey()).build(), STATE_CACHE);
case "huawei" -> new AuthHuaweiV3Request(builder.build(), STATE_CACHE); case "huawei" -> new AuthHuaweiV3Request(builder.build(), STATE_CACHE);
case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeV2Request(builder.agentId(obj.getAgentId()).build(), STATE_CACHE); case "wechat_enterprise" ->
new AuthWeChatEnterpriseQrcodeV2Request(builder.agentId(obj.getAgentId()).build(), STATE_CACHE);
case "gitlab" -> new AuthGitlabRequest(builder.build(), STATE_CACHE); case "gitlab" -> new AuthGitlabRequest(builder.build(), STATE_CACHE);
case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE); case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE);
case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
@@ -90,4 +92,3 @@ public class SocialUtils {
}; };
} }
} }
@@ -1,10 +1,6 @@
package org.dromara.common.translation.annotation; package org.dromara.common.translation.annotation;
import java.lang.annotation.Documented; import java.lang.annotation.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* 通用翻译注解 * 通用翻译注解
@@ -7,13 +7,7 @@ import org.dromara.common.translation.annotation.TranslationType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Arrays; import java.util.*;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
/** /**
@@ -88,8 +82,8 @@ public interface TranslationInterface<T> {
* *
* @param ids 原始 ID 字符串 * @param ids 原始 ID 字符串
* @param mapper ID 到值的映射函数 * @param mapper ID 到值的映射函数
* @return 拼接后的结果字符串
* @param <E> 值类型 * @param <E> 值类型
* @return 拼接后的结果字符串
*/ */
default <E> String joinMappedValues(String ids, Function<Long, E> mapper) { default <E> String joinMappedValues(String ids, Function<Long, E> mapper) {
return StreamUtils.join(parseLongIds(ids), id -> { return StreamUtils.join(parseLongIds(ids), id -> {
@@ -12,12 +12,7 @@ import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.core.TranslationInterface; import org.dromara.common.translation.core.TranslationInterface;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import java.util.Collections; import java.util.*;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 翻译响应处理器 * 翻译响应处理器
@@ -1,7 +1,7 @@
package org.dromara.common.web.config; package org.dromara.common.web.config;
import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil; import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.json.enhance.JsonValueEnhancer; import org.dromara.common.json.enhance.JsonValueEnhancer;
import org.dromara.common.web.advice.ResponseEnhancementAdvice; import org.dromara.common.web.advice.ResponseEnhancementAdvice;
@@ -50,14 +50,14 @@ public class ResourcesConfig implements WebMvcConfigurer {
public void addFormatters(FormatterRegistry registry) { public void addFormatters(FormatterRegistry registry) {
// 全局日期格式转换配置 // 全局日期格式转换配置
registry.addConverter(String.class, Date.class, source -> { registry.addConverter(String.class, Date.class, source -> {
DateTime parse = DateUtil.parse(source); DateTime parse = DateUtils.parse(source);
if (ObjectUtils.isNull(parse)) { if (ObjectUtils.isNull(parse)) {
return null; return null;
} }
return parse.toJdkDate(); return parse.toJdkDate();
}); });
registry.addConverter(String.class, LocalDateTime.class, source -> { registry.addConverter(String.class, LocalDateTime.class, source -> {
DateTime parse = DateUtil.parse(source); DateTime parse = DateUtils.parse(source);
if (ObjectUtils.isNull(parse)) { if (ObjectUtils.isNull(parse)) {
return null; return null;
} }
@@ -1,9 +1,9 @@
package org.dromara.common.web.core; package org.dromara.common.web.core;
import org.springframework.web.servlet.LocaleResolver;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.LocaleResolver;
import java.util.Locale; import java.util.Locale;
/** /**
@@ -1,10 +1,10 @@
package org.dromara.common.web.filter; package org.dromara.common.web.filter;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.http.MediaType;
import jakarta.servlet.*; import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.http.MediaType;
import java.io.IOException; import java.io.IOException;
/** /**
@@ -1,13 +1,12 @@
package org.dromara.common.web.filter; package org.dromara.common.web.filter;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import org.dromara.common.core.constant.Constants;
import jakarta.servlet.ReadListener; import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream; import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper; import jakarta.servlet.http.HttpServletRequestWrapper;
import org.dromara.common.core.constant.Constants;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -1,13 +1,13 @@
package org.dromara.common.web.filter; package org.dromara.common.web.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.web.config.properties.XssProperties; import org.dromara.common.web.config.properties.XssProperties;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -1,6 +1,7 @@
package org.dromara.common.web.handler; package org.dromara.common.web.handler;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpStatus; import cn.hutool.http.HttpStatus;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@@ -151,8 +152,9 @@ public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class) @ExceptionHandler(RuntimeException.class)
public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) { public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e); String errorId = RandomUtil.randomNumbers(8);
return R.fail("发生未知异常,请联系管理员"); log.error("请求地址'{}',发生未知异常, 错误编号: {}", requestURI, errorId, e);
return R.fail("发生未知异常,请联系管理员 [错误编号: " + errorId + "]");
} }
/** /**
@@ -161,8 +163,9 @@ public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
public R<Void> handleException(Exception e, HttpServletRequest request) { public R<Void> handleException(Exception e, HttpServletRequest request) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e); String errorId = RandomUtil.randomNumbers(8);
return R.fail("发生系统异常,请联系管理员"); log.error("请求地址'{}',发生系统异常, 错误编号: {}", requestURI, errorId, e);
return R.fail("发生系统异常,请联系管理员 [错误编号: " + errorId + "]");
} }
/** /**
@@ -1,11 +1,6 @@
package com.aizuda.snail.ai.starter.filter; package com.aizuda.snail.ai.starter.filter;
import jakarta.servlet.Filter; import jakarta.servlet.*;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
@@ -4,6 +4,10 @@ server:
context-path: /snail-ai context-path: /snail-ai
spring: spring:
application:
name: ruoyi-snailai-server
profiles:
active: @profiles.active@
servlet: servlet:
multipart: multipart:
enabled: true enabled: true
@@ -14,8 +18,7 @@ spring:
web: web:
resources: resources:
static-locations: classpath:admin/ static-locations: classpath:admin/
profiles:
active: @profiles.active@
snail-ai: snail-ai:
server: server:
grpc-port: 18888 grpc-port: 18888
@@ -1,8 +1,8 @@
package org.dromara.demo.controller; package org.dromara.demo.controller;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -1,36 +1,36 @@
package org.dromara.demo.controller; package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.PageResult;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup; import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.core.validate.QueryGroup; import org.dromara.common.core.validate.QueryGroup;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.redis.annotation.RepeatSubmit;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.PageResult;
import org.dromara.common.excel.core.ExcelResult; import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.excel.utils.ExcelBuilder;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.redis.annotation.RepeatSubmit;
import org.dromara.common.web.core.BaseController;
import org.dromara.demo.domain.TestDemo; import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.domain.bo.TestDemoBo; import org.dromara.demo.domain.bo.TestDemoBo;
import org.dromara.demo.domain.vo.TestDemoImportVo; import org.dromara.demo.domain.vo.TestDemoImportVo;
import org.dromara.demo.domain.vo.TestDemoVo; import org.dromara.demo.domain.vo.TestDemoVo;
import org.dromara.demo.service.ITestDemoService; import org.dromara.demo.service.ITestDemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse; import java.util.Arrays;
import jakarta.validation.constraints.NotEmpty; import java.util.List;
import jakarta.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@@ -1,17 +1,16 @@
package org.dromara.demo.controller; package org.dromara.demo.controller;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.MessageUtils;
import lombok.Data;
import org.hibernate.validator.constraints.Range; import org.hibernate.validator.constraints.Range;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/** /**
* 测试国际化 * 测试国际化
@@ -1,11 +1,11 @@
package org.dromara.demo.controller; package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.sensitive.annotation.Sensitive;
import org.dromara.common.sensitive.core.SensitiveStrategy;
import lombok.Data; import lombok.Data;
import org.dromara.common.core.domain.R;
import org.dromara.common.sensitive.annotation.Sensitive;
import org.dromara.common.sensitive.core.SensitiveService; import org.dromara.common.sensitive.core.SensitiveService;
import org.dromara.common.sensitive.core.SensitiveStrategy;
import org.dromara.common.web.core.BaseController;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -1,25 +1,25 @@
package org.dromara.demo.controller; package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup; import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.core.validate.QueryGroup; import org.dromara.common.core.validate.QueryGroup;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.excel.utils.ExcelBuilder;
import org.dromara.common.redis.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.redis.annotation.RepeatSubmit;
import org.dromara.common.web.core.BaseController;
import org.dromara.demo.domain.bo.TestTreeBo; import org.dromara.demo.domain.bo.TestTreeBo;
import org.dromara.demo.domain.vo.TestTreeVo; import org.dromara.demo.domain.vo.TestTreeVo;
import org.dromara.demo.service.ITestTreeService; import org.dromara.demo.service.ITestTreeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -1,10 +1,10 @@
package org.dromara.demo.controller.queue; package org.dromara.demo.controller.queue;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.QueueUtils;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.QueueUtils;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -1,10 +1,10 @@
package org.dromara.demo.domain; package org.dromara.demo.domain;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.enums.AlgorithmType;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.enums.AlgorithmType;
/** /**
* 测试加密字段实体 * 测试加密字段实体
@@ -1,12 +1,12 @@
package org.dromara.demo.domain.vo; package org.dromara.demo.domain.vo;
import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated;
import org.apache.fesod.sheet.annotation.ExcelProperty;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated;
import org.apache.fesod.sheet.annotation.ExcelProperty;
import org.dromara.common.core.enums.UserStatus; import org.dromara.common.core.enums.UserStatus;
import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup; import org.dromara.common.core.validate.EditGroup;
@@ -1,11 +1,10 @@
package org.dromara.demo.domain.vo; package org.dromara.demo.domain.vo;
import org.apache.fesod.sheet.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper; import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.apache.fesod.sheet.annotation.ExcelProperty;
import org.dromara.demo.domain.TestDemo; import org.dromara.demo.domain.TestDemo;
/** /**
@@ -1,5 +1,7 @@
package org.dromara.demo.domain.vo; package org.dromara.demo.domain.vo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated; import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated;
import org.apache.fesod.sheet.annotation.ExcelProperty; import org.apache.fesod.sheet.annotation.ExcelProperty;
import org.apache.fesod.sheet.annotation.format.DateTimeFormat; import org.apache.fesod.sheet.annotation.format.DateTimeFormat;
@@ -8,8 +10,6 @@ import org.dromara.common.excel.annotation.ExcelRequired;
import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant; import org.dromara.common.translation.constant.TransConstant;
import org.dromara.demo.domain.TestDemo; import org.dromara.demo.domain.TestDemo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
@@ -1,10 +1,10 @@
package org.dromara.demo.domain.vo; package org.dromara.demo.domain.vo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated; import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated;
import org.apache.fesod.sheet.annotation.ExcelProperty; import org.apache.fesod.sheet.annotation.ExcelProperty;
import org.dromara.demo.domain.TestTree; import org.dromara.demo.domain.TestTree;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;

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