From 8b97e7bc53e039f3985bbdfdce4c84e0ea114933 Mon Sep 17 00:00:00 2001
From: ColorDreams <545073804@qq.com>
Date: Wed, 24 Dec 2025 19:09:36 +0800
Subject: [PATCH 01/26] update ip2region version to 3.3.2
---
pom.xml | 2 +-
.../common/core/utils/ip/RegionUtils.java | 32 ++++++++-----------
2 files changed, 15 insertions(+), 19 deletions(-)
diff --git a/pom.xml b/pom.xml
index 0c175fda9..16e05d583 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,7 +38,7 @@
1.80
1.16.7
- 3.3.1
+ 3.3.2
2.28.22
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java
index 191b1e599..5c74a8351 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java
@@ -1,6 +1,5 @@
package org.dromara.common.core.utils.ip;
-import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
@@ -9,7 +8,6 @@ import org.lionsoul.ip2region.service.Config;
import org.lionsoul.ip2region.service.Ip2Region;
import org.lionsoul.ip2region.xdb.Util;
-import java.io.File;
import java.io.InputStream;
import java.time.Duration;
@@ -31,6 +29,11 @@ public class RegionUtils {
// 下载地址:https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v6.xdb
public static final String DEFAULT_IPV6_XDB_PATH = "ip2region_v6.xdb";
+ // 默认缓存切片大小为15MB(仅针对BufferCache全量读取有效,如果你的xdb数据库很大,合理设置该值可以有效提升BufferCache模式下的查询效率,具体可以查看Ip2Region的README)
+ // 注意:设置过大的值可能会申请内存时,因内存不足而导致OOM,请合理设置该值。
+ // README:https://gitee.com/lionsoul/ip2region/tree/master/binding/java
+ public static final int DEFAULT_CACHE_SLICE_BYTES = 1024 * 1024 * 15;
+
// 未知地址
public static final String UNKNOWN_ADDRESS = "未知";
@@ -43,20 +46,18 @@ public class RegionUtils {
// 注意:Ip2Region 的xdb文件加载策略 CachePolicy 有三种,分别是:BufferCache(全量读取xdb到内存中)、VIndexCache(默认策略,按需读取并缓存)、NoCache(实时读取)
// 本项目工具使用的 CachePolicy 为 BufferCache,BufferCache会加载整个xdb文件到内存中,setXdbInputStream 仅支持 BufferCache 策略。
// 因为加载整个xdb文件会耗费非常大的内存,如果你不希望加载整个xdb到内存中,更推荐使用 VIndexCache 或 NoCache(即实时读取文件)策略和 setXdbPath/setXdbFile 加载方法(需要注意的一点,setXdbPath 和 setXdbFile 不支持读取ClassPath(即源码和resource目录)中的文件)。
- // 一般而言,更建议把xdb数据库放到一个指定的文件目录中(即不打包进jar包中),然后使用 NoCache + 配合SearcherPool的并发池读取数据,更方便随时更新xdb数据库
+ // 一般而言,更建议把xdb数据库放到一个指定的文件目录中(即不打包进jar包中),然后使用 VIndexCache + 配合SearcherPool的并发池读取数据,更方便随时更新xdb数据库
- // TODO 2025年12月23日 Ip2Region封装的 InputStream 读取函数 Searcher.loadContentFromInputStream 在Linux环境下会申请过大的byte[]空间而导致OOM,这里先用临时文件的方案解决,等后续 Ip2Region 更新解决方案
- // 创建临时文件
- File v4TempXdb = FileUtil.writeFromStream(ResourceUtil.getStream(DEFAULT_IPV4_XDB_PATH), FileUtil.createTempFile());
+ InputStream v4InputStream = ResourceUtil.getStream(DEFAULT_IPV4_XDB_PATH);
// IPv4配置
Config v4Config = Config.custom()
.setCachePolicy(Config.BufferCache)
- .setXdbFile(v4TempXdb)
-// .setXdbInputStream(ResourceUtil.getStream(DEFAULT_IPV4_XDB_PATH))
+ //.setXdbFile(v4TempXdb)
+ .setXdbInputStream(v4InputStream)
+ //
+ .setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
.asV4();
- // 删除临时文件
- v4TempXdb.delete();
// IPv6配置
Config v6Config = null;
@@ -64,17 +65,12 @@ public class RegionUtils {
if (v6XdbInputStream == null) {
log.warn("未加载 IPv6 地址库:未在类路径下找到文件 {}。当前仅启用 IPv4 查询。如需启用 IPv6,请将 ip2region_v6.xdb 放置到 resources 目录", DEFAULT_IPV6_XDB_PATH);
} else {
- // 创建临时文件
- File v6TempXdb = FileUtil.writeFromStream(ResourceUtil.getStream(DEFAULT_IPV4_XDB_PATH), FileUtil.createTempFile());
-
v6Config = Config.custom()
.setCachePolicy(Config.BufferCache)
- .setXdbFile(v6TempXdb)
-// .setXdbInputStream(v6XdbInputStream)
+ //.setXdbFile(v6TempXdb)
+ .setXdbInputStream(v6XdbInputStream)
+ .setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
.asV6();
-
- // 删除临时文件
- v6TempXdb.delete();
}
// 初始化Ip2Region实例
From d634c2a292250c422997e1c1055e7a6b3870d207 Mon Sep 17 00:00:00 2001
From: AprilWind <2100166581@qq.com>
Date: Mon, 5 Jan 2026 14:39:40 +0800
Subject: [PATCH 02/26] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96oss=E6=97=A5?=
=?UTF-8?q?=E5=BF=97=E4=BE=A6=E5=90=AC=E5=99=A8=E6=89=93=E5=8D=B0=E7=BA=A7?=
=?UTF-8?q?=E5=88=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../dromara/common/oss/core/OssClient.java | 67 +++++++++++--------
1 file changed, 39 insertions(+), 28 deletions(-)
diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
index 089d1926b..14fc4dcfe 100644
--- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
+++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
@@ -141,7 +141,8 @@ public class OssClient {
try {
// 构建上传请求对象
FileUpload fileUpload = transferManager.uploadFile(
- x -> x.putObjectRequest(
+ x -> {
+ x.source(filePath).putObjectRequest(
y -> y.bucket(properties.getBucketName())
.key(key)
.contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null)
@@ -149,10 +150,13 @@ public class OssClient {
// 用于设置对象的访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
// 因此根据具体的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
//.acl(getAccessPolicy().getObjectCannedACL())
- .build())
- .addTransferListener(LoggingTransferListener.create())
- .source(filePath).build());
-
+ .build()
+ );
+ if (log.isDebugEnabled()) {
+ x.addTransferListener(LoggingTransferListener.create());
+ }
+ }
+ );
// 等待上传完成并获取上传结果
CompletedFileUpload uploadResult = fileUpload.completionFuture().join();
String eTag = uploadResult.response().eTag();
@@ -192,16 +196,21 @@ public class OssClient {
// 使用 transferManager 进行上传
Upload upload = transferManager.upload(
- x -> x.requestBody(body).addTransferListener(LoggingTransferListener.create())
- .putObjectRequest(
+ x -> {
+ x.requestBody(body).putObjectRequest(
y -> y.bucket(properties.getBucketName())
.key(key)
.contentType(contentType)
// 用于设置对象的访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
// 因此根据具体的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
//.acl(getAccessPolicy().getObjectCannedACL())
- .build())
- .build());
+ .build()
+ );
+ if (log.isDebugEnabled()) {
+ x.addTransferListener(LoggingTransferListener.create());
+ }
+ }
+ );
// 将输入流写入请求体
body.writeInputStream(inputStream);
@@ -229,13 +238,17 @@ public class OssClient {
Path tempFilePath = FileUtils.createTempFile().toPath();
// 使用 S3TransferManager 下载文件
FileDownload downloadFile = transferManager.downloadFile(
- x -> x.getObjectRequest(
+ x -> {
+ x.destination(tempFilePath).getObjectRequest(
y -> y.bucket(properties.getBucketName())
.key(removeBaseUrl(path))
- .build())
- .addTransferListener(LoggingTransferListener.create())
- .destination(tempFilePath)
- .build());
+ .build()
+ );
+ if (log.isDebugEnabled()) {
+ x.addTransferListener(LoggingTransferListener.create());
+ }
+ }
+ );
// 等待文件下载操作完成
downloadFile.completionFuture().join();
return tempFilePath;
@@ -244,8 +257,8 @@ public class OssClient {
/**
* 下载文件从 Amazon S3 到 输出流
*
- * @param key 文件在 Amazon S3 中的对象键
- * @param out 输出流
+ * @param key 文件在 Amazon S3 中的对象键
+ * @param out 输出流
* @param consumer 自定义处理逻辑
* @throws OssException 如果下载失败,抛出自定义异常
*/
@@ -260,26 +273,24 @@ public class OssClient {
/**
* 下载文件从 Amazon S3 到 输出流
*
- * @param key 文件在 Amazon S3 中的对象键
+ * @param key 文件在 Amazon S3 中的对象键
* @param contentLengthConsumer 文件大小消费者函数
* @return 写出订阅器
* @throws OssException 如果下载失败,抛出自定义异常
*/
public WriteOutSubscriber download(String key, Consumer contentLengthConsumer) {
try {
- // 构建下载请求
- DownloadRequest> publisherDownloadRequest = DownloadRequest.builder()
- // 文件对象
- .getObjectRequest(y -> y.bucket(properties.getBucketName())
- .key(key)
- .build())
- .addTransferListener(LoggingTransferListener.create())
+ DownloadRequest.TypedBuilder> typedBuilder = DownloadRequest.builder()
// 使用发布订阅转换器
.responseTransformer(AsyncResponseTransformer.toPublisher())
- .build();
+ // 文件对象
+ .getObjectRequest(y -> y.bucket(properties.getBucketName()).key(key).build());
+ if (log.isDebugEnabled()) {
+ typedBuilder.addTransferListener(LoggingTransferListener.create());
+ }
// 使用 S3TransferManager 下载文件
- Download> publisherDownload = transferManager.download(publisherDownloadRequest);
+ Download> publisherDownload = transferManager.download(typedBuilder.build());
// 获取下载发布订阅转换器
ResponsePublisher publisher = publisherDownload.completionFuture().join().result();
// 执行文件大小消费者函数
@@ -289,7 +300,7 @@ public class OssClient {
// 构建写出订阅器对象
return out -> {
// 创建可写入的字节通道
- try(WritableByteChannel channel = Channels.newChannel(out)){
+ try (WritableByteChannel channel = Channels.newChannel(out)) {
// 订阅数据
publisher.subscribe(byteBuffer -> {
while (byteBuffer.hasRemaining()) {
@@ -347,7 +358,7 @@ public class OssClient {
*
* @param objectKey 对象KEY
* @param expiredTime 链接授权到期时间
- * @param metadata 元数据
+ * @param metadata 元数据
*/
public String createPresignedPutUrl(String objectKey, Duration expiredTime, Map metadata) {
// 使用 AWS S3 预签名 URL 的生成器 获取上传文件对象的预签名 URL
From 2b8f4e1d2c9ed6d14aad84d3d05c4ffd369509b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Tue, 6 Jan 2026 17:22:44 +0800
Subject: [PATCH 03/26] =?UTF-8?q?update=20=E4=B8=8B=E6=9E=B6=E8=BF=87?=
=?UTF-8?q?=E6=9C=9F=E7=9A=84=E8=B5=9E=E5=8A=A9=E5=95=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index 457f0a0b5..8786e423e 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,6 @@ MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc
-**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/**
Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11
aizuda flowlong 工作流 - https://gitee.com/aizuda/flowlong
Ruoyi-Plus-Uniapp - https://ruoyi.plus
From bbc684b33518945472b09796554b0a896967e8b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Tue, 6 Jan 2026 17:26:45 +0800
Subject: [PATCH 04/26] =?UTF-8?q?update=20=E5=88=A0=E9=99=A4=E5=B7=B2?=
=?UTF-8?q?=E7=BB=8F=E8=BF=87=E6=9C=9F=E7=9A=84=E9=85=8D=E7=BD=AE=E7=B1=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../common/register/ServerRegister.java | 146 ------------------
1 file changed, 146 deletions(-)
delete mode 100644 ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/common/register/ServerRegister.java
diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/common/register/ServerRegister.java b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/common/register/ServerRegister.java
deleted file mode 100644
index 2a8a47aa4..000000000
--- a/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/common/register/ServerRegister.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package com.aizuda.snailjob.server.common.register;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.StrUtil;
-import com.aizuda.snailjob.common.core.enums.NodeTypeEnum;
-import com.aizuda.snailjob.common.core.util.JsonUtil;
-import com.aizuda.snailjob.common.core.util.NetUtil;
-import com.aizuda.snailjob.common.core.util.SnailJobVersion;
-import com.aizuda.snailjob.common.core.util.StreamUtils;
-import com.aizuda.snailjob.common.log.SnailJobLog;
-import com.aizuda.snailjob.server.common.cache.CacheConsumerGroup;
-import com.aizuda.snailjob.server.common.config.SystemProperties;
-import com.aizuda.snailjob.server.common.convert.RegisterNodeInfoConverter;
-import com.aizuda.snailjob.server.common.dto.ServerNodeExtAttrs;
-import com.aizuda.snailjob.server.common.handler.InstanceManager;
-import com.aizuda.snailjob.template.datasource.persistence.po.ServerNode;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.google.common.collect.Lists;
-import lombok.RequiredArgsConstructor;
-import org.springframework.boot.autoconfigure.web.ServerProperties;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-/**
- * 服务端注册
- *
- * @author opensnail
- * @date 2023-06-07
- * @since 1.6.0
- */
-@Component(ServerRegister.BEAN_NAME)
-@RequiredArgsConstructor
-public class ServerRegister extends AbstractRegister {
- public static final String BEAN_NAME = "serverRegister";
- private final ScheduledExecutorService serverRegisterNode = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "server-register-node"));
- public static final int DELAY_TIME = 30;
- public static final String CURRENT_CID;
- public static final String GROUP_NAME = "DEFAULT_SERVER";
- public static final String NAMESPACE_ID = "DEFAULT_SERVER_NAMESPACE_ID";
- private final InstanceManager instanceManager;
- private final SystemProperties systemProperties;
- private final ServerProperties serverProperties;
-
- static {
- CURRENT_CID = IdUtil.getSnowflakeNextIdStr();
- }
-
- @Override
- public boolean supports(int type) {
- return getNodeType().equals(type);
- }
-
- @Override
- protected void beforeProcessor(RegisterContext context) {
- // 新增扩展参数
- ServerNodeExtAttrs serverNodeExtAttrs = new ServerNodeExtAttrs();
- serverNodeExtAttrs.setWebPort(serverProperties.getPort());
- serverNodeExtAttrs.setSystemVersion(SnailJobVersion.getVersion());
-
- context.setGroupName(GROUP_NAME);
- context.setHostId(CURRENT_CID);
- String serverHost = systemProperties.getServerHost();
- if (StrUtil.isEmptyIfStr(serverHost)) {
- serverHost = NetUtil.getLocalIpStr();
- }
- context.setHostIp(serverHost);
- context.setHostPort(systemProperties.getServerPort());
- context.setContextPath(Optional.ofNullable(serverProperties.getServlet().getContextPath()).orElse(StrUtil.EMPTY));
- context.setNamespaceId(NAMESPACE_ID);
- context.setExtAttrs(JsonUtil.toJsonString(serverNodeExtAttrs));
- }
-
- @Override
- protected LocalDateTime getExpireAt() {
- return LocalDateTime.now().plusSeconds(DELAY_TIME);
- }
-
- @Override
- protected boolean doRegister(RegisterContext context, ServerNode serverNode) {
- refreshExpireAt(Lists.newArrayList(serverNode));
- return Boolean.TRUE;
- }
-
-
- @Override
- protected void afterProcessor(final ServerNode serverNode) {
- try {
- // 同步当前POD消费的组的节点信息
- // netty的client只会注册到一个服务端,若组分配的和client连接的不是一个POD则会导致当前POD没有其他客户端的注册信息
- ConcurrentMap/*namespaceId*/> allConsumerGroupName = CacheConsumerGroup.getAllConsumerGroupName();
- if (CollUtil.isNotEmpty(allConsumerGroupName)) {
- Set namespaceIdSets = StreamUtils.toSetByFlatMap(allConsumerGroupName.values(), Set::stream);
- if (CollUtil.isEmpty(namespaceIdSets)) {
- return;
- }
-
- List serverNodes = serverNodeMapper.selectList(
- new LambdaQueryWrapper()
- .eq(ServerNode::getNodeType, NodeTypeEnum.CLIENT.getType())
- .in(ServerNode::getNamespaceId, namespaceIdSets)
- .in(ServerNode::getGroupName, allConsumerGroupName.keySet()));
- for (final ServerNode node : serverNodes) {
- // 刷新全量本地缓存
- instanceManager.registerOrUpdate(RegisterNodeInfoConverter.INSTANCE.toRegisterNodeInfo(node));
- // 刷新过期时间
- CacheConsumerGroup.addOrUpdate(node.getGroupName(), node.getNamespaceId());
- }
- }
- } catch (Exception e) {
- SnailJobLog.LOCAL.error("Client refresh failed", e);
- }
- }
-
- @Override
- protected Integer getNodeType() {
- return NodeTypeEnum.SERVER.getType();
- }
-
- @Override
- public void start() {
- SnailJobLog.LOCAL.info("ServerRegister start");
-
- serverRegisterNode.scheduleAtFixedRate(() -> {
- try {
- this.register(new RegisterContext());
- } catch (Exception e) {
- SnailJobLog.LOCAL.error("Server-side registration failed", e);
- }
- }, 0, DELAY_TIME * 2 / 3, TimeUnit.SECONDS);
-
- }
-
- @Override
- public void close() {
- SnailJobLog.LOCAL.info("ServerRegister close");
- }
-}
From 1a14bdf256bfe4323946452741997bff1309b9f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Fri, 9 Jan 2026 11:50:28 +0800
Subject: [PATCH 05/26] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E7=BB=9F?=
=?UTF-8?q?=E4=B8=80=E7=94=A8=E8=AF=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../main/resources/i18n/messages.properties | 2 +-
.../resources/i18n/messages_zh_CN.properties | 2 +-
.../common/core/constant/CacheConstants.java | 2 +-
.../common/core/constant/CacheNames.java | 2 +-
.../common/core/service/UserService.java | 8 ++---
.../dromara/common/mail/utils/MailUtils.java | 30 +++++++++----------
.../common/satoken/utils/LoginHelper.java | 2 +-
.../monitor/SysLogininforController.java | 2 +-
.../service/impl/SysUserServiceImpl.java | 12 ++++----
script/sql/oracle/oracle_ry_vue_5.X.sql | 2 +-
script/sql/postgres/postgres_ry_vue_5.X.sql | 2 +-
script/sql/ry_vue_5.X.sql | 2 +-
script/sql/sqlserver/sqlserver_ry_vue_5.X.sql | 2 +-
13 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties
index f2777f77b..cc02929ef 100644
--- a/ruoyi-admin/src/main/resources/i18n/messages.properties
+++ b/ruoyi-admin/src/main/resources/i18n/messages.properties
@@ -13,7 +13,7 @@ user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
-user.username.length.valid=账户长度必须在{min}到{max}个字符之间
+user.username.length.valid=帐户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
diff --git a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
index f2777f77b..cc02929ef 100644
--- a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
+++ b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
@@ -13,7 +13,7 @@ user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
-user.username.length.valid=账户长度必须在{min}到{max}个字符之间
+user.username.length.valid=帐户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java
index ceb837044..ff590cf22 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java
@@ -23,7 +23,7 @@ public interface CacheConstants {
String SYS_DICT_KEY = "sys_dict:";
/**
- * 登录账户密码错误次数 redis key
+ * 登录帐户密码错误次数 redis key
*/
String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
index c38f39b47..dd9c750ca 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
@@ -47,7 +47,7 @@ public interface CacheNames {
String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
/**
- * 用户账户
+ * 用户帐户
*/
String SYS_USER_NAME = "sys_user_name#30d";
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
index eefeef011..10108d011 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
@@ -13,15 +13,15 @@ import java.util.Map;
public interface UserService {
/**
- * 通过用户ID查询用户账户
+ * 通过用户ID查询用户帐户
*
* @param userId 用户ID
- * @return 用户账户
+ * @return 用户帐户
*/
String selectUserNameById(Long userId);
/**
- * 通过用户ID查询用户账户
+ * 通过用户ID查询用户帐户
*
* @param userId 用户ID
* @return 用户名称
@@ -29,7 +29,7 @@ public interface UserService {
String selectNicknameById(Long userId);
/**
- * 通过用户ID查询用户账户
+ * 通过用户ID查询用户帐户
*
* @param userIds 用户ID 多个用逗号隔开
* @return 用户名称
diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java
index a28701fbc..918b42b57 100644
--- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java
+++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java
@@ -51,7 +51,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
+ * 使用配置文件中设置的帐户发送文本邮件,发送给单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -66,7 +66,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 使用配置文件中设置的帐户发送HTML邮件,发送给单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -81,7 +81,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 使用配置文件中设置的帐户发送邮件,发送单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -96,7 +96,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 使用配置文件中设置的帐户发送邮件,发送单个或多个收件人
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
@@ -114,7 +114,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送文本邮件,发送给多人
+ * 使用配置文件中设置的帐户发送文本邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -127,7 +127,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送HTML邮件,发送给多人
+ * 使用配置文件中设置的帐户发送HTML邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -141,7 +141,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送邮件,发送给多人
+ * 使用配置文件中设置的帐户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -155,7 +155,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送邮件,发送给多人
+ * 使用配置文件中设置的帐户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
@@ -223,7 +223,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 使用配置文件中设置的帐户发送HTML邮件,发送给单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -239,7 +239,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 使用配置文件中设置的帐户发送邮件,发送单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -255,7 +255,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 使用配置文件中设置的帐户发送邮件,发送单个或多个收件人
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
@@ -274,7 +274,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送HTML邮件,发送给多人
+ * 使用配置文件中设置的帐户发送HTML邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -289,7 +289,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送邮件,发送给多人
+ * 使用配置文件中设置的帐户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -304,7 +304,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的账户发送邮件,发送给多人
+ * 使用配置文件中设置的帐户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
@@ -380,7 +380,7 @@ public class MailUtils {
/**
* 根据配置文件,获取邮件客户端会话
*
- * @param mailAccount 邮件账户配置
+ * @param mailAccount 邮件帐户配置
* @param isSingleton 是否单例(全局共享会话)
* @return {@link Session}
* @since 5.5.7
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
index 730bae29d..ad3915ddc 100644
--- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
@@ -99,7 +99,7 @@ public class LoginHelper {
}
/**
- * 获取用户账户
+ * 获取用户帐户
*/
public static String getUsername() {
return Convert.toStr(getExtra(USER_NAME_KEY));
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java
index 378717003..ef871c90f 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java
@@ -79,7 +79,7 @@ public class SysLogininforController extends BaseController {
}
@SaCheckPermission("monitor:logininfor:unlock")
- @Log(title = "账户解锁", businessType = BusinessType.OTHER)
+ @Log(title = "帐户解锁", businessType = BusinessType.OTHER)
@RepeatSubmit()
@GetMapping("/unlock/{userName}")
public R unlock(@PathVariable("userName") String userName) {
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
index aafc73e29..c16cd9add 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
@@ -580,10 +580,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
- * 通过用户ID查询用户账户
+ * 通过用户ID查询用户帐户
*
* @param userId 用户ID
- * @return 用户账户
+ * @return 用户帐户
*/
@Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId")
@Override
@@ -594,10 +594,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
- * 通过用户ID查询用户账户
+ * 通过用户ID查询用户帐户
*
* @param userId 用户ID
- * @return 用户账户
+ * @return 用户帐户
*/
@Override
@Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId")
@@ -608,10 +608,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
- * 通过用户ID查询用户账户
+ * 通过用户ID查询用户帐户
*
* @param userIds 用户ID 多个用逗号隔开
- * @return 用户账户
+ * @return 用户帐户
*/
@Override
public String selectNicknameByIds(String userIds) {
diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql
index 041e99a57..627f4d666 100644
--- a/script/sql/oracle/oracle_ry_vue_5.X.sql
+++ b/script/sql/oracle/oracle_ry_vue_5.X.sql
@@ -518,7 +518,7 @@ insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1,
insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1050', '帐户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate, null, null, '');
-- 在线用户按钮
insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, sysdate, null, null, '');
diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql
index b5da8e096..78c51d9e0 100644
--- a/script/sql/postgres/postgres_ry_vue_5.X.sql
+++ b/script/sql/postgres/postgres_ry_vue_5.X.sql
@@ -519,7 +519,7 @@ insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', '1'
insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, now(), null, null, '');
-insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, now(), null, null, '');
+insert into sys_menu values('1050', '帐户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, now(), null, null, '');
-- 在线用户按钮
insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:query', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, now(), null, null, '');
diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql
index dae47920d..4b172e91f 100644
--- a/script/sql/ry_vue_5.X.sql
+++ b/script/sql/ry_vue_5.X.sql
@@ -353,7 +353,7 @@ insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1,
insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, sysdate(), null, null, '');
-insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1050', '帐户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate(), null, null, '');
-- 在线用户按钮
insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, sysdate(), null, null, '');
diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql
index 5db798848..a602122f5 100644
--- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql
+++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql
@@ -1801,7 +1801,7 @@ INSERT sys_menu VALUES (1044, N'登录删除', 501, 2, N'#', N'', N'', 1, 0, N'F
GO
INSERT sys_menu VALUES (1045, N'日志导出', 501, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:export', N'#', 103, 1, getdate(), NULL, NULL, N'')
GO
-INSERT sys_menu VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', 103, 1, getdate(), NULL, NULL, N'')
+INSERT sys_menu VALUES (1050, N'帐户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', 103, 1, getdate(), NULL, NULL, N'')
GO
INSERT sys_menu VALUES (1046, N'在线查询', 109, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:query', N'#', 103, 1, getdate(), NULL, NULL, N'')
GO
From 948eba656608eec0d1a9ddbb836612d5281f5142 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Fri, 9 Jan 2026 13:19:21 +0800
Subject: [PATCH 06/26] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E8=8F=9C=E5=8D=95=E8=B7=AF=E7=94=B1=E5=9C=B0=E5=9D=80?=
=?UTF-8?q?=E5=92=8C=E5=90=8D=E7=A7=B0=E7=9A=84=E6=A0=A1=E9=AA=8C=E8=A7=84?=
=?UTF-8?q?=E5=88=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/system/SysMenuController.java | 4 ++
.../org/dromara/system/domain/SysMenu.java | 10 ++--
.../system/service/ISysMenuService.java | 9 ++++
.../service/impl/SysMenuServiceImpl.java | 47 ++++++++++++++++++-
4 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java
index d1b5b9bd5..9d95880e8 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java
@@ -137,6 +137,8 @@ public class SysMenuController extends BaseController {
return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
} else if (SystemConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
+ } else if (!menuService.checkRouteConfigUnique(menu)) {
+ return R.fail("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
}
return toAjax(menuService.insertMenu(menu));
}
@@ -156,6 +158,8 @@ public class SysMenuController extends BaseController {
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
} else if (menu.getMenuId().equals(menu.getParentId())) {
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
+ } else if (!menuService.checkRouteConfigUnique(menu)) {
+ return R.fail("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
}
return toAjax(menuService.updateMenu(menu));
}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMenu.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMenu.java
index 2df55962b..5fe0de585 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMenu.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMenu.java
@@ -130,11 +130,11 @@ public class SysMenu extends BaseEntity {
public String getRouterPath() {
String routerPath = this.path;
// 内链打开外网方式
- if (getParentId() != 0L && isInnerLink()) {
+ if (!Constants.TOP_PARENT_ID.equals(getParentId()) && isInnerLink()) {
routerPath = innerLinkReplaceEach(routerPath);
}
// 非外链并且是一级目录(类型为目录)
- if (0L == getParentId() && SystemConstants.TYPE_DIR.equals(getMenuType())
+ if (Constants.TOP_PARENT_ID.equals(getParentId()) && SystemConstants.TYPE_DIR.equals(getMenuType())
&& SystemConstants.NO_FRAME.equals(getIsFrame())) {
routerPath = "/" + this.path;
}
@@ -152,7 +152,7 @@ public class SysMenu extends BaseEntity {
String component = SystemConstants.LAYOUT;
if (StringUtils.isNotEmpty(this.component) && !isMenuFrame()) {
component = this.component;
- } else if (StringUtils.isEmpty(this.component) && getParentId() != 0L && isInnerLink()) {
+ } else if (StringUtils.isEmpty(this.component) && !Constants.TOP_PARENT_ID.equals(getParentId()) && isInnerLink()) {
component = SystemConstants.INNER_LINK;
} else if (StringUtils.isEmpty(this.component) && isParentView()) {
component = SystemConstants.PARENT_VIEW;
@@ -164,7 +164,7 @@ public class SysMenu extends BaseEntity {
* 是否为菜单内部跳转
*/
public boolean isMenuFrame() {
- return getParentId() == 0L && SystemConstants.TYPE_MENU.equals(menuType) && isFrame.equals(SystemConstants.NO_FRAME);
+ return Constants.TOP_PARENT_ID.equals(getParentId()) && SystemConstants.TYPE_MENU.equals(menuType) && isFrame.equals(SystemConstants.NO_FRAME);
}
/**
@@ -178,7 +178,7 @@ public class SysMenu extends BaseEntity {
* 是否为parent_view组件
*/
public boolean isParentView() {
- return getParentId() != 0L && SystemConstants.TYPE_DIR.equals(menuType);
+ return !Constants.TOP_PARENT_ID.equals(getParentId()) && SystemConstants.TYPE_DIR.equals(menuType);
}
/**
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java
index f972691be..8888c3c39 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java
@@ -160,4 +160,13 @@ public interface ISysMenuService {
* @return 结果
*/
boolean checkMenuNameUnique(SysMenuBo menu);
+
+ /**
+ * 校验路由组合是否唯一
+ *
+ * @param menu 菜单信息
+ * @return 结果
+ */
+ boolean checkRouteConfigUnique(SysMenuBo menu);
+
}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
index bbba6a01e..b048d2a81 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
@@ -6,6 +6,7 @@ import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.utils.MapstructUtils;
@@ -29,13 +30,17 @@ import org.dromara.system.service.ISysMenuService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
/**
* 菜单 业务层处理
*
* @author Lion Li
*/
+@Slf4j
@RequiredArgsConstructor
@Service
public class SysMenuServiceImpl implements ISysMenuService {
@@ -353,6 +358,46 @@ public class SysMenuServiceImpl implements ISysMenuService {
return !exist;
}
+ /**
+ * 校验路由名称是否唯一
+ *
+ * @param menuBo 菜单信息
+ * @return 结果
+ */
+ @Override
+ public boolean checkRouteConfigUnique(SysMenuBo menuBo) {
+ SysMenu menu = MapstructUtils.convert(menuBo, SysMenu.class);
+ long menuId = ObjectUtil.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
+ Long parentId = menu.getParentId();
+ String path = menu.getPath();
+ String routeName = StringUtils.isEmpty(menu.getRouteName()) ? path : menu.getRouteName();
+ List sysMenuList = baseMapper.selectList(
+ new LambdaQueryWrapper()
+ .in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU)
+ .and(w ->
+ w.eq(SysMenu::getPath, path)
+ .or().eq(SysMenu::getRouteName, path)
+ .or().eq(SysMenu::getRouteName, routeName)));
+ for (SysMenu sysMenu : sysMenuList) {
+ if (sysMenu.getMenuId() != menuId) {
+ Long dbParentId = sysMenu.getParentId();
+ String dbPath = sysMenu.getPath();
+ String dbRouteName = StringUtils.isEmpty(sysMenu.getRouteName()) ? dbPath : sysMenu.getRouteName();
+ if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == dbParentId.longValue()) {
+ log.warn("[同级路由冲突] 同级下已存在相同路由路径 '{}',冲突菜单:{}", dbPath, sysMenu.getMenuName());
+ return false;
+ } else if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == Constants.TOP_PARENT_ID) {
+ log.warn("[根目录路由冲突] 根目录下路由 '{}' 必须唯一,已被菜单 '{}' 占用", path, sysMenu.getMenuName());
+ return false;
+ } else if (StringUtils.equalsAnyIgnoreCase(routeName, dbRouteName)) {
+ log.warn("[路由名称冲突] 路由名称 '{}' 需全局唯一,已被菜单 '{}' 使用", routeName, sysMenu.getMenuName());
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
/**
* 根据父节点的ID获取所有子节点
*
From d8ed23f2270c22a191864a82641657784f91293f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Fri, 9 Jan 2026 17:10:51 +0800
Subject: [PATCH 07/26] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E8=8F=9C=E5=8D=95=E8=B7=AF=E7=94=B1=E5=9C=B0=E5=9D=80?=
=?UTF-8?q?=E5=92=8C=E5=90=8D=E7=A7=B0=E7=9A=84=E6=A0=A1=E9=AA=8C=E8=A7=84?=
=?UTF-8?q?=E5=88=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/dromara/system/service/impl/SysMenuServiceImpl.java | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
index b048d2a81..7a844812c 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
@@ -374,10 +374,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
List sysMenuList = baseMapper.selectList(
new LambdaQueryWrapper()
.in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU)
- .and(w ->
- w.eq(SysMenu::getPath, path)
- .or().eq(SysMenu::getRouteName, path)
- .or().eq(SysMenu::getRouteName, routeName)));
+ .eq(SysMenu::getPath, path));
for (SysMenu sysMenu : sysMenuList) {
if (sysMenu.getMenuId() != menuId) {
Long dbParentId = sysMenu.getParentId();
From 0940ba67621496a66c94564cb13e4cf026865daa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Mon, 12 Jan 2026 09:17:39 +0800
Subject: [PATCH 08/26] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=A4=A7?=
=?UTF-8?q?=E5=AE=B6=E9=83=BD=E8=AE=A4=E5=8F=AF=E7=94=A8"=E8=B4=A6"?=
=?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=94=B9=E4=B8=BA=E8=B4=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../main/resources/i18n/messages.properties | 8 ++--
.../resources/i18n/messages_zh_CN.properties | 8 ++--
.../common/core/constant/CacheConstants.java | 2 +-
.../common/core/constant/CacheNames.java | 2 +-
.../common/core/domain/dto/UserDTO.java | 2 +-
.../common/core/service/UserService.java | 8 ++--
.../dromara/common/mail/utils/MailUtils.java | 40 +++++++++----------
.../common/satoken/utils/LoginHelper.java | 2 +-
.../monitor/SysLogininforController.java | 2 +-
.../org/dromara/system/domain/SysUser.java | 2 +-
.../dromara/system/domain/bo/SysUserBo.java | 2 +-
.../system/domain/vo/SysUserExportVo.java | 4 +-
.../system/domain/vo/SysUserImportVo.java | 4 +-
.../dromara/system/domain/vo/SysUserVo.java | 2 +-
.../system/service/ISysUserService.java | 2 +-
.../service/impl/SysUserServiceImpl.java | 14 +++----
script/sql/oracle/oracle_ry_vue_5.X.sql | 4 +-
script/sql/postgres/postgres_ry_vue_5.X.sql | 4 +-
script/sql/ry_vue_5.X.sql | 4 +-
script/sql/sqlserver/sqlserver_ry_vue_5.X.sql | 4 +-
20 files changed, 60 insertions(+), 60 deletions(-)
diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties
index cc02929ef..e1e5263b8 100644
--- a/ruoyi-admin/src/main/resources/i18n/messages.properties
+++ b/ruoyi-admin/src/main/resources/i18n/messages.properties
@@ -5,7 +5,7 @@ user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
-user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
+user.password.retry.limit.exceed=密码输入错误{0}次,账户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
@@ -13,7 +13,7 @@ user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
-user.username.length.valid=帐户长度必须在{min}到{max}个字符之间
+user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
@@ -47,10 +47,10 @@ repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
-sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
+sms.code.retry.limit.exceed=短信验证码输入错误{0}次,账户锁定{1}分钟
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
-email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
+email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,账户锁定{1}分钟
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
diff --git a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
index cc02929ef..e1e5263b8 100644
--- a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
+++ b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
@@ -5,7 +5,7 @@ user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
-user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
+user.password.retry.limit.exceed=密码输入错误{0}次,账户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
@@ -13,7 +13,7 @@ user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
-user.username.length.valid=帐户长度必须在{min}到{max}个字符之间
+user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
@@ -47,10 +47,10 @@ repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
-sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
+sms.code.retry.limit.exceed=短信验证码输入错误{0}次,账户锁定{1}分钟
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
-email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
+email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,账户锁定{1}分钟
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java
index ff590cf22..ceb837044 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java
@@ -23,7 +23,7 @@ public interface CacheConstants {
String SYS_DICT_KEY = "sys_dict:";
/**
- * 登录帐户密码错误次数 redis key
+ * 登录账户密码错误次数 redis key
*/
String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
index dd9c750ca..c38f39b47 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
@@ -47,7 +47,7 @@ public interface CacheNames {
String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
/**
- * 用户帐户
+ * 用户账户
*/
String SYS_USER_NAME = "sys_user_name#30d";
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java
index cb5def9a9..393a0f0de 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java
@@ -61,7 +61,7 @@ public class UserDTO implements Serializable {
private String sex;
/**
- * 帐号状态(0正常 1停用)
+ * 账号状态(0正常 1停用)
*/
private String status;
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
index 10108d011..eefeef011 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
@@ -13,15 +13,15 @@ import java.util.Map;
public interface UserService {
/**
- * 通过用户ID查询用户帐户
+ * 通过用户ID查询用户账户
*
* @param userId 用户ID
- * @return 用户帐户
+ * @return 用户账户
*/
String selectUserNameById(Long userId);
/**
- * 通过用户ID查询用户帐户
+ * 通过用户ID查询用户账户
*
* @param userId 用户ID
* @return 用户名称
@@ -29,7 +29,7 @@ public interface UserService {
String selectNicknameById(Long userId);
/**
- * 通过用户ID查询用户帐户
+ * 通过用户ID查询用户账户
*
* @param userIds 用户ID 多个用逗号隔开
* @return 用户名称
diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java
index 918b42b57..793c0241c 100644
--- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java
+++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java
@@ -51,7 +51,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送文本邮件,发送给单个或多个收件人
+ * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -66,7 +66,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送HTML邮件,发送给单个或多个收件人
+ * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -81,7 +81,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送邮件,发送单个或多个收件人
+ * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -96,7 +96,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送邮件,发送单个或多个收件人
+ * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
@@ -114,7 +114,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送文本邮件,发送给多人
+ * 使用配置文件中设置的账户发送文本邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -127,7 +127,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送HTML邮件,发送给多人
+ * 使用配置文件中设置的账户发送HTML邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -141,7 +141,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送邮件,发送给多人
+ * 使用配置文件中设置的账户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -155,7 +155,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送邮件,发送给多人
+ * 使用配置文件中设置的账户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
@@ -192,7 +192,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
- * @param mailAccount 邮件帐户信息
+ * @param mailAccount 邮件账户信息
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
@@ -207,7 +207,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
- * @param mailAccount 邮件帐户信息
+ * @param mailAccount 邮件账户信息
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
* @param bccs 密送人列表,可以为null或空
@@ -223,7 +223,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送HTML邮件,发送给单个或多个收件人
+ * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -239,7 +239,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送邮件,发送单个或多个收件人
+ * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -255,7 +255,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送邮件,发送单个或多个收件人
+ * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
@@ -274,7 +274,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送HTML邮件,发送给多人
+ * 使用配置文件中设置的账户发送HTML邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -289,7 +289,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送邮件,发送给多人
+ * 使用配置文件中设置的账户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param subject 标题
@@ -304,7 +304,7 @@ public class MailUtils {
}
/**
- * 使用配置文件中设置的帐户发送邮件,发送给多人
+ * 使用配置文件中设置的账户发送邮件,发送给多人
*
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
@@ -343,7 +343,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
- * @param mailAccount 邮件帐户信息
+ * @param mailAccount 邮件账户信息
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
@@ -360,7 +360,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
- * @param mailAccount 邮件帐户信息
+ * @param mailAccount 邮件账户信息
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
* @param bccs 密送人列表,可以为null或空
@@ -380,7 +380,7 @@ public class MailUtils {
/**
* 根据配置文件,获取邮件客户端会话
*
- * @param mailAccount 邮件帐户配置
+ * @param mailAccount 邮件账户配置
* @param isSingleton 是否单例(全局共享会话)
* @return {@link Session}
* @since 5.5.7
@@ -400,7 +400,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
- * @param mailAccount 邮件帐户信息
+ * @param mailAccount 邮件账户信息
* @param useGlobalSession 是否全局共享Session
* @param tos 收件人列表
* @param ccs 抄送人列表,可以为null或空
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
index ad3915ddc..730bae29d 100644
--- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
@@ -99,7 +99,7 @@ public class LoginHelper {
}
/**
- * 获取用户帐户
+ * 获取用户账户
*/
public static String getUsername() {
return Convert.toStr(getExtra(USER_NAME_KEY));
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java
index ef871c90f..378717003 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java
@@ -79,7 +79,7 @@ public class SysLogininforController extends BaseController {
}
@SaCheckPermission("monitor:logininfor:unlock")
- @Log(title = "帐户解锁", businessType = BusinessType.OTHER)
+ @Log(title = "账户解锁", businessType = BusinessType.OTHER)
@RepeatSubmit()
@GetMapping("/unlock/{userName}")
public R unlock(@PathVariable("userName") String userName) {
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java
index 3712f805f..a06172f66 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java
@@ -78,7 +78,7 @@ public class SysUser extends TenantEntity {
private String password;
/**
- * 帐号状态(0正常 1停用)
+ * 账号状态(0正常 1停用)
*/
private String status;
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java
index 1472d2428..11c01666d 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java
@@ -78,7 +78,7 @@ public class SysUserBo extends BaseEntity {
private String password;
/**
- * 帐号状态(0正常 1停用)
+ * 账号状态(0正常 1停用)
*/
private String status;
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java
index 913ab2001..c60087202 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java
@@ -63,9 +63,9 @@ public class SysUserExportVo implements Serializable {
private String sex;
/**
- * 帐号状态(0正常 1停用)
+ * 账号状态(0正常 1停用)
*/
- @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class)
+ @ExcelProperty(value = "账号状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_normal_disable")
private String status;
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java
index d4e575c14..4507f631e 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java
@@ -67,9 +67,9 @@ public class SysUserImportVo implements Serializable {
private String sex;
/**
- * 帐号状态(0正常 1停用)
+ * 账号状态(0正常 1停用)
*/
- @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class)
+ @ExcelProperty(value = "账号状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_normal_disable")
private String status;
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java
index 86249d20e..755dcf81d 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java
@@ -89,7 +89,7 @@ public class SysUserVo implements Serializable {
private String password;
/**
- * 帐号状态(0正常 1停用)
+ * 账号状态(0正常 1停用)
*/
private String status;
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java
index 1fe554547..9e255f992 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java
@@ -174,7 +174,7 @@ public interface ISysUserService {
* 修改用户状态
*
* @param userId 用户ID
- * @param status 帐号状态
+ * @param status 账号状态
* @return 结果
*/
int updateUserStatus(Long userId, String status);
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
index c16cd9add..39ad4cb91 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
@@ -375,7 +375,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
* 修改用户状态
*
* @param userId 用户ID
- * @param status 帐号状态
+ * @param status 账号状态
* @return 结果
*/
@Override
@@ -580,10 +580,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
- * 通过用户ID查询用户帐户
+ * 通过用户ID查询用户账户
*
* @param userId 用户ID
- * @return 用户帐户
+ * @return 用户账户
*/
@Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId")
@Override
@@ -594,10 +594,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
- * 通过用户ID查询用户帐户
+ * 通过用户ID查询用户账户
*
* @param userId 用户ID
- * @return 用户帐户
+ * @return 用户账户
*/
@Override
@Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId")
@@ -608,10 +608,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
}
/**
- * 通过用户ID查询用户帐户
+ * 通过用户ID查询用户账户
*
* @param userIds 用户ID 多个用逗号隔开
- * @return 用户帐户
+ * @return 用户账户
*/
@Override
public String selectNicknameByIds(String userIds) {
diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql
index 627f4d666..3a9cef456 100644
--- a/script/sql/oracle/oracle_ry_vue_5.X.sql
+++ b/script/sql/oracle/oracle_ry_vue_5.X.sql
@@ -257,7 +257,7 @@ comment on column sys_user.phonenumber is '手机号码';
comment on column sys_user.sex is '用户性别(0男 1女 2未知)';
comment on column sys_user.avatar is '头像路径';
comment on column sys_user.password is '密码';
-comment on column sys_user.status is '帐号状态(0正常 1停用)';
+comment on column sys_user.status is '账号状态(0正常 1停用)';
comment on column sys_user.del_flag is '删除标志(0代表存在 1代表删除)';
comment on column sys_user.login_ip is '最后登录IP';
comment on column sys_user.login_date is '最后登录时间';
@@ -518,7 +518,7 @@ insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1,
insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu values('1050', '帐户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate, null, null, '');
-- 在线用户按钮
insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, sysdate, null, null, '');
insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, sysdate, null, null, '');
diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql
index 78c51d9e0..bb127f9e2 100644
--- a/script/sql/postgres/postgres_ry_vue_5.X.sql
+++ b/script/sql/postgres/postgres_ry_vue_5.X.sql
@@ -258,7 +258,7 @@ comment on column sys_user.phonenumber is '手机号码';
comment on column sys_user.sex is '用户性别(0男 1女 2未知)';
comment on column sys_user.avatar is '头像地址';
comment on column sys_user.password is '密码';
-comment on column sys_user.status is '帐号状态(0正常 1停用)';
+comment on column sys_user.status is '账号状态(0正常 1停用)';
comment on column sys_user.del_flag is '删除标志(0代表存在 1代表删除)';
comment on column sys_user.login_ip is '最后登陆IP';
comment on column sys_user.login_date is '最后登陆时间';
@@ -519,7 +519,7 @@ insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', '1'
insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, now(), null, null, '');
-insert into sys_menu values('1050', '帐户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, now(), null, null, '');
+insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, now(), null, null, '');
-- 在线用户按钮
insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:query', '#', 103, 1, now(), null, null, '');
insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, now(), null, null, '');
diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql
index 4b172e91f..3cd642b0b 100644
--- a/script/sql/ry_vue_5.X.sql
+++ b/script/sql/ry_vue_5.X.sql
@@ -148,7 +148,7 @@ create table sys_user (
sex char(1) default '0' comment '用户性别(0男 1女 2未知)',
avatar bigint(20) comment '头像地址',
password varchar(100) default '' comment '密码',
- status char(1) default '0' comment '帐号状态(0正常 1停用)',
+ status char(1) default '0' comment '账号状态(0正常 1停用)',
del_flag char(1) default '0' comment '删除标志(0代表存在 1代表删除)',
login_ip varchar(128) default '' comment '最后登录IP',
login_date datetime comment '最后登录时间',
@@ -353,7 +353,7 @@ insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1,
insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, sysdate(), null, null, '');
-insert into sys_menu values('1050', '帐户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate(), null, null, '');
-- 在线用户按钮
insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, sysdate(), null, null, '');
diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql
index a602122f5..295cc8e36 100644
--- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql
+++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql
@@ -1801,7 +1801,7 @@ INSERT sys_menu VALUES (1044, N'登录删除', 501, 2, N'#', N'', N'', 1, 0, N'F
GO
INSERT sys_menu VALUES (1045, N'日志导出', 501, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:export', N'#', 103, 1, getdate(), NULL, NULL, N'')
GO
-INSERT sys_menu VALUES (1050, N'帐户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', 103, 1, getdate(), NULL, NULL, N'')
+INSERT sys_menu VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', 103, 1, getdate(), NULL, NULL, N'')
GO
INSERT sys_menu VALUES (1046, N'在线查询', 109, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:query', N'#', 103, 1, getdate(), NULL, NULL, N'')
GO
@@ -2802,7 +2802,7 @@ EXEC sys.sp_addextendedproperty
'COLUMN', N'password'
GO
EXEC sys.sp_addextendedproperty
- 'MS_Description', N'帐号状态(0正常 1停用)' ,
+ 'MS_Description', N'账号状态(0正常 1停用)' ,
'SCHEMA', N'dbo',
'TABLE', N'sys_user',
'COLUMN', N'status'
From 459e9caf14a3ca681d2282413908063a1b1870fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Mon, 12 Jan 2026 09:21:01 +0800
Subject: [PATCH 09/26] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=85=BC?=
=?UTF-8?q?=E5=AE=B9path=E5=A4=A7=E5=86=99=E5=BC=80=E5=A4=B4=E6=90=9C?=
=?UTF-8?q?=E7=B4=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/dromara/system/service/impl/SysMenuServiceImpl.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
index 7a844812c..854c836d2 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
@@ -374,7 +374,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
List sysMenuList = baseMapper.selectList(
new LambdaQueryWrapper()
.in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU)
- .eq(SysMenu::getPath, path));
+ .eq(SysMenu::getPath, path).or().eq(SysMenu::getPath, routeName));
for (SysMenu sysMenu : sysMenuList) {
if (sysMenu.getMenuId() != menuId) {
Long dbParentId = sysMenu.getParentId();
From c5777c01c1ac9b5829918dd1ec56a9db71f10bec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BE=A1=E6=B0=91Coding?=
Date: Mon, 12 Jan 2026 06:01:54 +0000
Subject: [PATCH 10/26] =?UTF-8?q?!822=20fix:=20=E6=96=87=E6=A1=88=E9=94=99?=
=?UTF-8?q?=E8=AF=AF=20*=20fix:=20=E6=96=87=E6=A1=88=E9=94=99=E8=AF=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/dromara/system/controller/system/SysMenuController.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java
index 9d95880e8..7d0ca0068 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java
@@ -159,7 +159,7 @@ public class SysMenuController extends BaseController {
} else if (menu.getMenuId().equals(menu.getParentId())) {
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
} else if (!menuService.checkRouteConfigUnique(menu)) {
- return R.fail("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
+ return R.fail("修改菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
}
return toAjax(menuService.updateMenu(menu));
}
From 7f9e4e14f0013e85dc8a48d908e902986292ee2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Wed, 14 Jan 2026 09:11:31 +0800
Subject: [PATCH 11/26] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E9=A1=B6?=
=?UTF-8?q?=E8=8A=82=E7=82=B9=E5=88=A4=E6=96=AD=E6=9D=A1=E4=BB=B6=E7=BC=BA?=
=?UTF-8?q?=E5=A4=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/dromara/system/service/impl/SysMenuServiceImpl.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
index 854c836d2..921f7e4b4 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
@@ -383,7 +383,9 @@ public class SysMenuServiceImpl implements ISysMenuService {
if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == dbParentId.longValue()) {
log.warn("[同级路由冲突] 同级下已存在相同路由路径 '{}',冲突菜单:{}", dbPath, sysMenu.getMenuName());
return false;
- } else if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == Constants.TOP_PARENT_ID) {
+ } else if (StringUtils.equalsAnyIgnoreCase(path, dbPath)
+ && parentId.longValue() == Constants.TOP_PARENT_ID
+ && dbParentId.longValue() == Constants.TOP_PARENT_ID) {
log.warn("[根目录路由冲突] 根目录下路由 '{}' 必须唯一,已被菜单 '{}' 占用", path, sysMenu.getMenuName());
return false;
} else if (StringUtils.equalsAnyIgnoreCase(routeName, dbRouteName)) {
From 2d4685ac5fe7f10cc4a067dbcee130e401a53f0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Wed, 14 Jan 2026 16:38:17 +0800
Subject: [PATCH 12/26] =?UTF-8?q?update=20=E4=BF=AE=E6=94=B9=E9=AA=8C?=
=?UTF-8?q?=E8=AF=81=E7=A0=81=E9=BB=98=E8=AE=A4=E6=A0=B7=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ruoyi-admin/src/main/resources/application.yml | 2 +-
.../main/java/org/dromara/common/web/config/CaptchaConfig.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 866b8f1a2..3fc344e0b 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -26,7 +26,7 @@ captcha:
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
- category: CIRCLE
+ category: SHEAR
# 数字验证码位数
numberLength: 1
# 字符验证码长度
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
index 650517ed1..b7172e9b6 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
@@ -23,7 +23,7 @@ public class CaptchaConfig {
private static final int WIDTH = 160;
private static final int HEIGHT = 60;
- private static final Color BACKGROUND = Color.LIGHT_GRAY;
+ private static final Color BACKGROUND = Color.WHITE;
private static final Font FONT = new Font("Arial", Font.BOLD, 48);
/**
From 6f94095bb0d0cbd64066d5ea1b15a5072e1eaa6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Wed, 14 Jan 2026 18:28:37 +0800
Subject: [PATCH 13/26] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E8=87=AA?=
=?UTF-8?q?=E8=A1=8C=E5=AE=9E=E7=8E=B0=E6=9B=B4=E6=BC=82=E4=BA=AE=E7=9A=84?=
=?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E5=9B=BE=E6=A1=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../web/controller/CaptchaController.java | 21 +-
.../src/main/resources/application.yml | 4 +-
.../common/web/config/CaptchaConfig.java | 49 -----
.../config/properties/CaptchaProperties.java | 9 +-
.../common/web/core/WaveAndCircleCaptcha.java | 197 ++++++++++++++++++
.../common/web/enums/CaptchaCategory.java | 35 ----
.../dromara/common/web/enums/CaptchaType.java | 29 ---
7 files changed, 211 insertions(+), 133 deletions(-)
create mode 100644 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/WaveAndCircleCaptcha.java
delete mode 100644 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaCategory.java
delete mode 100644 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaType.java
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
index dcd04c5e1..2586addab 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
@@ -1,8 +1,9 @@
package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
-import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
+import cn.hutool.captcha.generator.MathGenerator;
+import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import jakarta.validation.constraints.NotBlank;
@@ -14,14 +15,13 @@ import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.mail.config.properties.MailProperties;
import org.dromara.common.mail.utils.MailUtils;
import org.dromara.common.ratelimiter.annotation.RateLimiter;
import org.dromara.common.ratelimiter.enums.LimitType;
import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.web.core.WaveAndCircleCaptcha;
import org.dromara.common.web.config.properties.CaptchaProperties;
-import org.dromara.common.web.enums.CaptchaType;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
@@ -33,6 +33,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
+import java.awt.*;
import java.time.Duration;
import java.util.LinkedHashMap;
@@ -130,19 +131,21 @@ public class CaptchaController {
String uuid = IdUtil.simpleUUID();
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
// 生成验证码
- CaptchaType captchaType = captchaProperties.getType();
+ String captchaType = captchaProperties.getType();
CodeGenerator codeGenerator;
- if (CaptchaType.MATH == captchaType) {
- codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), captchaProperties.getNumberLength(), false);
+ if ("math".equals(captchaType)) {
+ codeGenerator = new MathGenerator(captchaProperties.getNumberLength(), false);
} else {
- codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), captchaProperties.getCharLength());
+ codeGenerator = new RandomGenerator(captchaProperties.getCharLength());
}
- AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
+ WaveAndCircleCaptcha captcha = new WaveAndCircleCaptcha(160, 60);
+ // captcha.setBackground(Color.WHITE); // 不设置就是透明底
+ captcha.setFont(new Font("Arial", Font.BOLD, 45));
captcha.setGenerator(codeGenerator);
captcha.createCode();
// 如果是数学验证码,使用SpEL表达式处理验证码结果
String code = captcha.getCode();
- if (CaptchaType.MATH == captchaType) {
+ if ("math".equals(captchaType)) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
code = exp.getValue(String.class);
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 3fc344e0b..d11d9f0ea 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -24,9 +24,7 @@ captcha:
# 是否启用验证码校验
enable: true
# 验证码类型 math 数组计算 char 字符验证
- type: MATH
- # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
- category: SHEAR
+ type: math
# 数字验证码位数
numberLength: 1
# 字符验证码长度
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
index b7172e9b6..7140db947 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
@@ -1,16 +1,8 @@
package org.dromara.common.web.config;
-import cn.hutool.captcha.CaptchaUtil;
-import cn.hutool.captcha.CircleCaptcha;
-import cn.hutool.captcha.LineCaptcha;
-import cn.hutool.captcha.ShearCaptcha;
import org.dromara.common.web.config.properties.CaptchaProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Lazy;
-
-import java.awt.*;
/**
* 验证码配置
@@ -21,45 +13,4 @@ import java.awt.*;
@EnableConfigurationProperties(CaptchaProperties.class)
public class CaptchaConfig {
- private static final int WIDTH = 160;
- private static final int HEIGHT = 60;
- private static final Color BACKGROUND = Color.WHITE;
- private static final Font FONT = new Font("Arial", Font.BOLD, 48);
-
- /**
- * 圆圈干扰验证码
- */
- @Lazy
- @Bean
- public CircleCaptcha circleCaptcha() {
- CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT);
- captcha.setBackground(BACKGROUND);
- captcha.setFont(FONT);
- return captcha;
- }
-
- /**
- * 线段干扰的验证码
- */
- @Lazy
- @Bean
- public LineCaptcha lineCaptcha() {
- LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT);
- captcha.setBackground(BACKGROUND);
- captcha.setFont(FONT);
- return captcha;
- }
-
- /**
- * 扭曲干扰验证码
- */
- @Lazy
- @Bean
- public ShearCaptcha shearCaptcha() {
- ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT);
- captcha.setBackground(BACKGROUND);
- captcha.setFont(FONT);
- return captcha;
- }
-
}
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CaptchaProperties.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CaptchaProperties.java
index bfc52f450..6dcfe64bb 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CaptchaProperties.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CaptchaProperties.java
@@ -1,7 +1,5 @@
package org.dromara.common.web.config.properties;
-import org.dromara.common.web.enums.CaptchaCategory;
-import org.dromara.common.web.enums.CaptchaType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -19,12 +17,7 @@ public class CaptchaProperties {
/**
* 验证码类型
*/
- private CaptchaType type;
-
- /**
- * 验证码类别
- */
- private CaptchaCategory category;
+ private String type;
/**
* 数字验证码位数
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/WaveAndCircleCaptcha.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/WaveAndCircleCaptcha.java
new file mode 100644
index 000000000..8b37be471
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/WaveAndCircleCaptcha.java
@@ -0,0 +1,197 @@
+package org.dromara.common.web.core;
+
+import cn.hutool.captcha.AbstractCaptcha;
+import cn.hutool.captcha.generator.CodeGenerator;
+import cn.hutool.captcha.generator.RandomGenerator;
+import cn.hutool.core.img.GraphicsUtil;
+import cn.hutool.core.img.ImgUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.RandomUtil;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.Serial;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * 带干扰线、波浪、圆的验证码
+ *
+ * @author Lion Li
+ */
+public class WaveAndCircleCaptcha extends AbstractCaptcha {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ // 构造方法(略,与之前一致)
+ public WaveAndCircleCaptcha(int width, int height) {
+ this(width, height, 4);
+ }
+
+ public WaveAndCircleCaptcha(int width, int height, int codeCount) {
+ this(width, height, codeCount, 6);
+ }
+
+ public WaveAndCircleCaptcha(int width, int height, int codeCount, int interfereCount) {
+ this(width, height, new RandomGenerator(codeCount), interfereCount);
+ }
+
+ public WaveAndCircleCaptcha(int width, int height, CodeGenerator generator, int interfereCount) {
+ super(width, height, generator, interfereCount);
+ }
+
+ public WaveAndCircleCaptcha(int width, int height, int codeCount, int interfereCount, float size) {
+ super(width, height, new RandomGenerator(codeCount), interfereCount, size);
+ }
+
+ @Override
+ public Image createImage(String code) {
+ final BufferedImage image = new BufferedImage(
+ width,
+ height,
+ (null == this.background) ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_INT_RGB
+ );
+ final Graphics2D g = ImgUtil.createGraphics(image, this.background);
+
+ try {
+ drawString(g, code);
+ // 扭曲
+ shear(g, this.width, this.height, ObjectUtil.defaultIfNull(this.background, Color.WHITE));
+ drawInterfere(g);
+ } finally {
+ g.dispose();
+ }
+
+ return image;
+ }
+
+ private void drawString(Graphics2D g, String code) {
+ // 设置抗锯齿(让字体渲染更清晰)
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+ if (this.textAlpha != null) {
+ g.setComposite(this.textAlpha);
+ }
+
+ GraphicsUtil.drawStringColourful(g, code, this.font, this.width, this.height);
+ }
+
+ protected void drawInterfere(Graphics2D g) {
+ ThreadLocalRandom random = RandomUtil.getRandom();
+ int circleCount = Math.max(0, this.interfereCount - 1);
+
+ // 圈圈
+ for (int i = 0; i < circleCount; i++) {
+ g.setColor(ImgUtil.randomColor(random));
+ int x = random.nextInt(width);
+ int y = random.nextInt(height);
+ int w = random.nextInt(height >> 1);
+ int h = random.nextInt(height >> 1);
+ g.drawOval(x, y, w, h);
+ }
+
+ // 仅 1 条平滑波浪线
+ if (this.interfereCount >= 1) {
+ g.setColor(getRandomColor(120, 230, random));
+ drawSmoothWave(g, random);
+ }
+ }
+
+ private void drawSmoothWave(Graphics2D g, ThreadLocalRandom random) {
+ int amplitude = random.nextInt(8) + 5; // 波动幅度
+ int wavelength = random.nextInt(40) + 30; // 波长
+ double phase = random.nextDouble() * Math.PI * 2;
+
+ // ✅ 关键:限制 baseY 在中间区域
+ int centerY = height / 2;
+ int verticalJitter = Math.max(5, height / 6); // 至少偏移5像素
+ int baseY = centerY - verticalJitter + random.nextInt(verticalJitter * 2);
+
+ g.setStroke(new BasicStroke(2.5f)); // 线宽
+
+ int[] xPoints = new int[width];
+ int[] yPoints = new int[width];
+ for (int x = 0; x < width; x++) {
+ int y = baseY + (int) (amplitude * Math.sin((double) x / wavelength * 2 * Math.PI + phase));
+ // 限制 y 不要超出图像边界(可选)
+ y = Math.max(amplitude, Math.min(y, height - amplitude));
+ xPoints[x] = x;
+ yPoints[x] = y;
+ }
+ g.drawPolyline(xPoints, yPoints, width);
+ }
+
+ private Color getRandomColor(int min, int max, ThreadLocalRandom random) {
+ int range = max - min;
+ return new Color(
+ min + random.nextInt(range),
+ min + random.nextInt(range),
+ min + random.nextInt(range)
+ );
+ }
+
+ /**
+ * 扭曲
+ *
+ * @param g {@link Graphics}
+ * @param w1 w1
+ * @param h1 h1
+ * @param color 颜色
+ */
+ private void shear(Graphics g, int w1, int h1, Color color) {
+ shearX(g, w1, h1, color);
+ shearY(g, w1, h1, color);
+ }
+
+ /**
+ * X坐标扭曲
+ *
+ * @param g {@link Graphics}
+ * @param w1 宽
+ * @param h1 高
+ * @param color 颜色
+ */
+ private void shearX(Graphics g, int w1, int h1, Color color) {
+
+ int period = RandomUtil.randomInt(this.width);
+
+ int frames = 1;
+ int phase = RandomUtil.randomInt(2);
+
+ for (int i = 0; i < h1; i++) {
+ double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
+ g.copyArea(0, i, w1, 1, (int) d, 0);
+ g.setColor(color);
+ g.drawLine((int) d, i, 0, i);
+ g.drawLine((int) d + w1, i, w1, i);
+ }
+
+ }
+
+ /**
+ * Y坐标扭曲
+ *
+ * @param g {@link Graphics}
+ * @param w1 宽
+ * @param h1 高
+ * @param color 颜色
+ */
+ private void shearY(Graphics g, int w1, int h1, Color color) {
+
+ int period = RandomUtil.randomInt(this.height >> 1);
+
+ int frames = 20;
+ int phase = 7;
+ for (int i = 0; i < w1; i++) {
+ double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
+ g.copyArea(i, 0, 1, h1, 0, (int) d);
+ g.setColor(color);
+ // 擦除原位置的痕迹
+ g.drawLine(i, (int) d, i, 0);
+ g.drawLine(i, (int) d + h1, i, h1);
+ }
+
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaCategory.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaCategory.java
deleted file mode 100644
index ecf26583c..000000000
--- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaCategory.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.dromara.common.web.enums;
-
-import cn.hutool.captcha.AbstractCaptcha;
-import cn.hutool.captcha.CircleCaptcha;
-import cn.hutool.captcha.LineCaptcha;
-import cn.hutool.captcha.ShearCaptcha;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 验证码类别
- *
- * @author Lion Li
- */
-@Getter
-@AllArgsConstructor
-public enum CaptchaCategory {
-
- /**
- * 线段干扰
- */
- LINE(LineCaptcha.class),
-
- /**
- * 圆圈干扰
- */
- CIRCLE(CircleCaptcha.class),
-
- /**
- * 扭曲干扰
- */
- SHEAR(ShearCaptcha.class);
-
- private final Class extends AbstractCaptcha> clazz;
-}
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaType.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaType.java
deleted file mode 100644
index d0b6ca3eb..000000000
--- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaType.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.dromara.common.web.enums;
-
-import cn.hutool.captcha.generator.CodeGenerator;
-import cn.hutool.captcha.generator.MathGenerator;
-import cn.hutool.captcha.generator.RandomGenerator;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 验证码类型
- *
- * @author Lion Li
- */
-@Getter
-@AllArgsConstructor
-public enum CaptchaType {
-
- /**
- * 数字
- */
- MATH(MathGenerator.class),
-
- /**
- * 字符
- */
- CHAR(RandomGenerator.class);
-
- private final Class extends CodeGenerator> clazz;
-}
From f984f08a14e495cf2f0138cc175c37f5bb7f33b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Fri, 16 Jan 2026 16:47:12 +0800
Subject: [PATCH 14/26] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E6=8C=89?=
=?UTF-8?q?=E9=92=AE=E8=8F=9C=E5=8D=95=20=E4=B8=8D=E5=BA=94=E8=AF=A5?=
=?UTF-8?q?=E6=A0=A1=E9=AA=8C=E8=B7=AF=E7=94=B1=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/dromara/system/service/impl/SysMenuServiceImpl.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
index 921f7e4b4..fd824e9af 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
@@ -367,6 +367,9 @@ public class SysMenuServiceImpl implements ISysMenuService {
@Override
public boolean checkRouteConfigUnique(SysMenuBo menuBo) {
SysMenu menu = MapstructUtils.convert(menuBo, SysMenu.class);
+ if (SystemConstants.TYPE_BUTTON.equals(menu.getMenuType())) {
+ return true;
+ }
long menuId = ObjectUtil.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
Long parentId = menu.getParentId();
String path = menu.getPath();
From 79d9f47053b0b42b54d068861583cb044fa647dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Mon, 19 Jan 2026 15:09:07 +0800
Subject: [PATCH 15/26] update README.md
---
README.md | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 8786e423e..983997a74 100644
--- a/README.md
+++ b/README.md
@@ -8,10 +8,9 @@
[](https://github.com/dromara/RuoYi-Vue-Plus)
[](https://gitcode.com/dromara/RuoYi-Vue-Plus)
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
-[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
-[]()
+[]()
[]()
[]()
@@ -33,7 +32,7 @@
MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
-数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/
+数舵科技 软件定制开发APP小程序等 - https://www.shuduokeji.com/
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc
Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11
aizuda flowlong 工作流 - https://gitee.com/aizuda/flowlong
From aa277b373bb3e7b106ae40ee1feccdab36f03d70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Wed, 21 Jan 2026 11:46:33 +0800
Subject: [PATCH 16/26] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E4=B8=8D?=
=?UTF-8?q?=E5=90=8C=E7=B1=BB=E5=88=AB=E8=8F=9C=E5=8D=95=E7=9A=84=E5=88=A4?=
=?UTF-8?q?=E6=96=AD=E9=80=BB=E8=BE=91=E6=9C=89=E8=AF=AF=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../system/service/impl/SysMenuServiceImpl.java | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
index fd824e9af..0c69b1f3f 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
@@ -377,21 +377,24 @@ public class SysMenuServiceImpl implements ISysMenuService {
List sysMenuList = baseMapper.selectList(
new LambdaQueryWrapper()
.in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU)
- .eq(SysMenu::getPath, path).or().eq(SysMenu::getPath, routeName));
+ .and(w ->
+ w.eq(SysMenu::getPath, path).or().eq(SysMenu::getPath, routeName)
+ ));
for (SysMenu sysMenu : sysMenuList) {
- if (sysMenu.getMenuId() != menuId) {
+ if (!sysMenu.getMenuId().equals(menuId)) {
Long dbParentId = sysMenu.getParentId();
String dbPath = sysMenu.getPath();
String dbRouteName = StringUtils.isEmpty(sysMenu.getRouteName()) ? dbPath : sysMenu.getRouteName();
- if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == dbParentId.longValue()) {
+ if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.equals(dbParentId)) {
log.warn("[同级路由冲突] 同级下已存在相同路由路径 '{}',冲突菜单:{}", dbPath, sysMenu.getMenuName());
return false;
} else if (StringUtils.equalsAnyIgnoreCase(path, dbPath)
- && parentId.longValue() == Constants.TOP_PARENT_ID
- && dbParentId.longValue() == Constants.TOP_PARENT_ID) {
+ && Constants.TOP_PARENT_ID.equals(parentId)
+ && Constants.TOP_PARENT_ID.equals(dbParentId)) {
log.warn("[根目录路由冲突] 根目录下路由 '{}' 必须唯一,已被菜单 '{}' 占用", path, sysMenu.getMenuName());
return false;
- } else if (StringUtils.equalsAnyIgnoreCase(routeName, dbRouteName)) {
+ } else if (StringUtils.equalsAnyIgnoreCase(routeName, dbRouteName)
+ && sysMenu.getMenuType().equals(menu.getMenuType())) {
log.warn("[路由名称冲突] 路由名称 '{}' 需全局唯一,已被菜单 '{}' 使用", routeName, sysMenu.getMenuName());
return false;
}
From 95c9e37797771945a9f8f0812c98cb0b69fa9ab0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Wed, 21 Jan 2026 11:51:43 +0800
Subject: [PATCH 17/26] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20oss=20?=
=?UTF-8?q?=E4=BE=9D=E8=B5=96=E6=B3=A8=E9=87=8A=E8=AF=B4=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ruoyi-common/ruoyi-common-oss/pom.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/ruoyi-common/ruoyi-common-oss/pom.xml b/ruoyi-common/ruoyi-common-oss/pom.xml
index 190dc5d46..bd19cc8d2 100644
--- a/ruoyi-common/ruoyi-common-oss/pom.xml
+++ b/ruoyi-common/ruoyi-common-oss/pom.xml
@@ -31,7 +31,7 @@
software.amazon.awssdk
s3
-
+
software.amazon.awssdk
aws-crt-client
@@ -49,13 +49,13 @@
-
+
software.amazon.awssdk
netty-nio-client
-
+
software.amazon.awssdk
s3-transfer-manager
From 76218091ad664a7fd3ed3b4adedd21a6f04d3e39 Mon Sep 17 00:00:00 2001
From: Coast <591616450@qq.com>
Date: Wed, 21 Jan 2026 06:41:34 +0000
Subject: [PATCH 18/26] =?UTF-8?q?!824=20update=20=E4=BC=98=E5=8C=96=20oss?=
=?UTF-8?q?=20=E4=BE=9D=E8=B5=96=E6=B3=A8=E9=87=8A=E8=AF=B4=E6=98=8E=20*?=
=?UTF-8?q?=20update=20=E4=BC=98=E5=8C=96=20oss=20=E4=BE=9D=E8=B5=96?=
=?UTF-8?q?=E6=B3=A8=E9=87=8A=E8=AF=B4=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 16e05d583..0bb01ff2e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -226,13 +226,13 @@
s3
${aws.sdk.version}
-
+
software.amazon.awssdk
s3-transfer-manager
${aws.sdk.version}
-
+
software.amazon.awssdk
netty-nio-client
From 348d7fc534461d5996301384bd4edbd87c408247 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Fri, 23 Jan 2026 13:48:07 +0800
Subject: [PATCH 19/26] update springboot 3.5.9 => 3.5.10 update springdoc
2.8.14 => 2.8.15 update mybatis-plus 3.5.14 => 3.5.16 update hutool 5.8.40 =>
5.8.43 update spring-boot-admin 3.5.5 => 3.5.6
---
pom.xml | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/pom.xml b/pom.xml
index 0bb01ff2e..756e8fed5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,27 +14,27 @@
5.5.2
- 3.5.9
+ 3.5.10
UTF-8
UTF-8
17
- 3.5.16
- 2.8.14
+ 3.5.19
+ 2.8.15
0.15.0
1.3.0
2.3
1.44.0
- 3.5.14
+ 3.5.16
3.9.1
- 5.8.40
- 3.5.5
+ 5.8.43
+ 3.5.6
3.52.0
2.2.7
4.3.1
1.9.0
1.5.0
0.2.0
- 1.18.40
+ 1.18.42
1.80
1.16.7
From 1bb597b8553db92ec495c6f000b80af79cfc83aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Fri, 23 Jan 2026 14:00:24 +0800
Subject: [PATCH 20/26] =?UTF-8?q?=F0=9F=A7=A8=F0=9F=A7=A8=F0=9F=A7=A8?=
=?UTF-8?q?=E5=8F=91=E5=B8=83=205.5.3=20=E7=89=88=E6=9C=AC=20=E6=8F=90?=
=?UTF-8?q?=E5=89=8D=E7=A5=9D=E5=A4=A7=E5=AE=B6=E6=96=B0=E5=B9=B4=E5=BF=AB?=
=?UTF-8?q?=E4=B9=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.run/ruoyi-monitor-admin.run.xml | 2 +-
.run/ruoyi-server.run.xml | 2 +-
.run/ruoyi-snailjob-server.run.xml | 2 +-
README.md | 2 +-
pom.xml | 2 +-
ruoyi-common/ruoyi-common-bom/pom.xml | 2 +-
script/docker/docker-compose.yml | 8 ++++----
7 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/.run/ruoyi-monitor-admin.run.xml b/.run/ruoyi-monitor-admin.run.xml
index 751f46f7f..e7d616db7 100644
--- a/.run/ruoyi-monitor-admin.run.xml
+++ b/.run/ruoyi-monitor-admin.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/ruoyi-server.run.xml b/.run/ruoyi-server.run.xml
index 2adc33a19..2d2c4471f 100644
--- a/.run/ruoyi-server.run.xml
+++ b/.run/ruoyi-server.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/ruoyi-snailjob-server.run.xml b/.run/ruoyi-snailjob-server.run.xml
index 442c2748b..5fe048db6 100644
--- a/.run/ruoyi-snailjob-server.run.xml
+++ b/.run/ruoyi-snailjob-server.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/README.md b/README.md
index 983997a74..acd14004c 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
[](https://gitcode.com/dromara/RuoYi-Vue-Plus)
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
-[](https://gitee.com/dromara/RuoYi-Vue-Plus)
+[](https://gitee.com/dromara/RuoYi-Vue-Plus)
[]()
[]()
[]()
diff --git a/pom.xml b/pom.xml
index 756e8fed5..5db1e6540 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
Dromara RuoYi-Vue-Plus多租户管理系统
- 5.5.2
+ 5.5.3
3.5.10
UTF-8
UTF-8
diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml
index 3324cbe5f..dd043681c 100644
--- a/ruoyi-common/ruoyi-common-bom/pom.xml
+++ b/ruoyi-common/ruoyi-common-bom/pom.xml
@@ -14,7 +14,7 @@
- 5.5.2
+ 5.5.3
diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml
index 462650bd5..f40a4ff01 100644
--- a/script/docker/docker-compose.yml
+++ b/script/docker/docker-compose.yml
@@ -99,7 +99,7 @@ services:
network_mode: "host"
ruoyi-server1:
- image: ruoyi/ruoyi-server:5.5.2
+ image: ruoyi/ruoyi-server:5.5.3
container_name: ruoyi-server1
environment:
# 时区上海
@@ -115,7 +115,7 @@ services:
network_mode: "host"
ruoyi-server2:
- image: ruoyi/ruoyi-server:5.5.2
+ image: ruoyi/ruoyi-server:5.5.3
container_name: ruoyi-server2
environment:
# 时区上海
@@ -131,7 +131,7 @@ services:
network_mode: "host"
ruoyi-monitor-admin:
- image: ruoyi/ruoyi-monitor-admin:5.5.2
+ image: ruoyi/ruoyi-monitor-admin:5.5.3
container_name: ruoyi-monitor-admin
environment:
# 时区上海
@@ -143,7 +143,7 @@ services:
network_mode: "host"
ruoyi-snailjob-server:
- image: ruoyi/ruoyi-snailjob-server:5.5.2
+ image: ruoyi/ruoyi-snailjob-server:5.5.3
container_name: ruoyi-snailjob-server
environment:
# 时区上海
From 34c3b8119012f4493f95121f8dc35b4036f14cc8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Tue, 27 Jan 2026 16:10:02 +0800
Subject: [PATCH 21/26] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20springboot?=
=?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=88=B03.5.10=E4=B9=8B=E5=90=8E=E5=A4=A7?=
=?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0=E8=AF=B7=E6=B1=82=E6=97=A0?=
=?UTF-8?q?=E5=93=8D=E5=BA=94=E9=97=AE=E9=A2=98(=E4=B8=8D=E6=B8=85?=
=?UTF-8?q?=E6=A5=9A=E5=8E=9F=E5=9B=A0=E7=AD=89spring=E4=BF=AE=E5=A4=8D)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 5db1e6540..d18e0a62b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
5.5.3
- 3.5.10
+ 3.5.9
UTF-8
UTF-8
17
From 48ea66cb1af46a1886210d7f9c90deed42a30faa Mon Sep 17 00:00:00 2001
From: ColorDreams <545073804@qq.com>
Date: Wed, 28 Jan 2026 13:12:56 +0800
Subject: [PATCH 22/26] =?UTF-8?q?update=20=E4=BD=BF=E7=94=A8release?=
=?UTF-8?q?=E6=8C=87=E4=BB=A4=E4=BB=A3=E6=9B=BFsource=E5=92=8Ctarget?=
=?UTF-8?q?=E6=8C=87=E4=BB=A4=E8=BF=9B=E8=A1=8C=E7=BC=96=E8=AF=91=E6=9E=84?=
=?UTF-8?q?=E5=BB=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index d18e0a62b..c2a1c44b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -375,8 +375,7 @@
maven-compiler-plugin
${maven-compiler-plugin.version}
- ${java.version}
- ${java.version}
+ ${java.version}
${project.build.sourceEncoding}
From cd0ee3f0169de8d4dfead8e5057cdcc51241137c Mon Sep 17 00:00:00 2001
From: ColorDreams <545073804@qq.com>
Date: Wed, 28 Jan 2026 13:14:35 +0800
Subject: [PATCH 23/26] =?UTF-8?q?update=20=E6=9B=B4=E6=96=B0ip2region?=
=?UTF-8?q?=E7=89=88=E6=9C=AC=EF=BC=8C=E4=BC=98=E5=8C=96IP=E6=9C=AA?=
=?UTF-8?q?=E7=9F=A5=E5=9C=B0=E5=8C=BA=E5=8D=A0=E4=BD=8D=E7=AC=A6=E4=B8=BA?=
=?UTF-8?q?0=E7=9A=84=E6=83=85=E5=86=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 2 +-
.../src/main/resources/ip2region_v4.xdb | Bin 11042429 -> 10646838 bytes
.../common/core/utils/ip/RegionUtils.java | 8 ++++----
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/pom.xml b/pom.xml
index c2a1c44b6..03f385f1f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,7 +38,7 @@
1.80
1.16.7
- 3.3.2
+ 3.3.4
2.28.22
diff --git a/ruoyi-admin/src/main/resources/ip2region_v4.xdb b/ruoyi-admin/src/main/resources/ip2region_v4.xdb
index 6f86c7d9b5ea27ecbe808e2be198bac1aaee0b92..707ea3d457cc4924ffb00182052f8d2963b5f476 100644
GIT binary patch
literal 10646838
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