Compare commits

...

2 Commits

Author SHA1 Message Date
疯狂的狮子Li
948eba6566 update 优化 添加菜单路由地址和名称的校验规则 2026-01-09 13:19:21 +08:00
疯狂的狮子Li
1a14bdf256 update 优化 统一用词 2026-01-09 11:50:28 +08:00
17 changed files with 99 additions and 41 deletions

View File

@@ -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个字符

View File

@@ -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个字符

View File

@@ -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:";

View File

@@ -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";

View File

@@ -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 用户名称

View File

@@ -51,7 +51,7 @@ public class MailUtils {
}
/**
* 使用配置文件中设置的户发送文本邮件,发送给单个或多个收件人<br>
* 使用配置文件中设置的户发送文本邮件,发送给单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -66,7 +66,7 @@ public class MailUtils {
}
/**
* 使用配置文件中设置的户发送HTML邮件发送给单个或多个收件人<br>
* 使用配置文件中设置的户发送HTML邮件发送给单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -81,7 +81,7 @@ public class MailUtils {
}
/**
* 使用配置文件中设置的户发送邮件,发送单个或多个收件人<br>
* 使用配置文件中设置的户发送邮件,发送单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -96,7 +96,7 @@ public class MailUtils {
}
/**
* 使用配置文件中设置的户发送邮件,发送单个或多个收件人<br>
* 使用配置文件中设置的户发送邮件,发送单个或多个收件人<br>
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @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邮件发送给单个或多个收件人<br>
* 使用配置文件中设置的户发送HTML邮件发送给单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -239,7 +239,7 @@ public class MailUtils {
}
/**
* 使用配置文件中设置的户发送邮件,发送单个或多个收件人<br>
* 使用配置文件中设置的户发送邮件,发送单个或多个收件人<br>
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @param to 收件人
@@ -255,7 +255,7 @@ public class MailUtils {
}
/**
* 使用配置文件中设置的户发送邮件,发送单个或多个收件人<br>
* 使用配置文件中设置的户发送邮件,发送单个或多个收件人<br>
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
*
* @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

View File

@@ -99,7 +99,7 @@ public class LoginHelper {
}
/**
* 获取用户
* 获取用户
*/
public static String getUsername() {
return Convert.toStr(getExtra(USER_NAME_KEY));

View File

@@ -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<Void> unlock(@PathVariable("userName") String userName) {

View File

@@ -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));
}

View File

@@ -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);
}
/**

View File

@@ -160,4 +160,13 @@ public interface ISysMenuService {
* @return 结果
*/
boolean checkMenuNameUnique(SysMenuBo menu);
/**
* 校验路由组合是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
boolean checkRouteConfigUnique(SysMenuBo menu);
}

View File

@@ -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<SysMenu> sysMenuList = baseMapper.selectList(
new LambdaQueryWrapper<SysMenu>()
.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获取所有子节点
*

View File

@@ -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) {

View File

@@ -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, '');

View File

@@ -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, '');

View File

@@ -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, '');

View File

@@ -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