feat(menu): 添加菜单唯一标识功能

- 新增 menuKey 字段到 SysMenu 实体类
- 在菜单服务接口和服务实现中添加 checkMenuKeyUnique、selectMenuByMenuKey 和 generateMenuKeys 方法
- 在控制器中增加 getByKey 和 generateMenuKeys 接口用于通过唯一标识获取菜单和批量生成菜单标识
- 在数据库映射中添加对 menuKey 字段的支持
- 在新增和修改菜单时验证 menuKey 唯一性
- 实现自动生成菜单唯一标识的功能,支持基于路径或菜单名称生成
This commit is contained in:
2026-07-01 13:23:18 +08:00
parent ad1fa08561
commit 62f6362d61
6 changed files with 159 additions and 1 deletions

View File

@@ -79,4 +79,16 @@ public interface SysMenuMapper extends BaseMapperPlus<SysMenuMapper, SysMenu, Sy
*/
List<Long> selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly);
/**
* 根据 menuKey 查询菜单
*
* @param menuKey 菜单唯一标识
* @return 菜单信息
*/
default SysMenu selectMenuByMenuKey(String menuKey) {
LambdaQueryWrapper<SysMenu> lqw = new LambdaQueryWrapper<SysMenu>()
.eq(SysMenu::getMenuKey, menuKey);
return this.selectOne(lqw);
}
}

View File

@@ -134,4 +134,27 @@ public interface ISysMenuService {
* @return 结果
*/
boolean checkMenuNameUnique(SysMenu menu);
/**
* 校验 menu_key 是否唯一
*
* @param menu 菜单信息
* @return 结果 true=唯一 false=不唯一
*/
boolean checkMenuKeyUnique(SysMenu menu);
/**
* 根据 menuKey 查询菜单
*
* @param menuKey 菜单唯一标识
* @return 菜单信息
*/
SysMenu selectMenuByMenuKey(String menuKey);
/**
* 为现有菜单自动生成 menu_key仅处理 menu_key 为空且 menu_type 为 C/M 的记录)
*
* @return 更新条数
*/
int generateMenuKeys();
}

View File

@@ -303,6 +303,92 @@ public class SysMenuServiceImpl implements ISysMenuService {
return !exist;
}
/**
* 校验 menu_key 是否唯一
*/
@Override
public boolean checkMenuKeyUnique(SysMenu menu) {
if (StringUtils.isBlank(menu.getMenuKey())) {
return true;
}
boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysMenu>()
.eq(SysMenu::getMenuKey, menu.getMenuKey())
.ne(ObjectUtil.isNotNull(menu.getMenuId()), SysMenu::getMenuId, menu.getMenuId()));
return !exist;
}
/**
* 根据 menuKey 查询菜单
*/
@Override
public SysMenu selectMenuByMenuKey(String menuKey) {
return baseMapper.selectMenuByMenuKey(menuKey);
}
/**
* 为现有菜单自动生成 menu_key
* 规则:
* C菜单去掉 path 的首尾 /,剩余 / 替换为 _如 /mes/qc/tensile → mes_qc_tensile
* M目录同上若 path 为空则用 menu_name 转拼音首字母
* F按钮不处理
*/
@Override
public int generateMenuKeys() {
List<SysMenu> allMenus = baseMapper.selectList(new LambdaQueryWrapper<SysMenu>()
.isNull(SysMenu::getMenuKey)
.in(SysMenu::getMenuType, UserConstants.TYPE_DIR, UserConstants.TYPE_MENU)
.eq(SysMenu::getStatus, UserConstants.MENU_NORMAL));
int count = 0;
for (SysMenu menu : allMenus) {
String key = buildMenuKey(menu);
if (StringUtils.isBlank(key)) {
continue;
}
// 确保唯一:如果已有同 key 的菜单,追加数字后缀
String uniqueKey = key;
int suffix = 1;
while (baseMapper.exists(new LambdaQueryWrapper<SysMenu>()
.eq(SysMenu::getMenuKey, uniqueKey)
.ne(SysMenu::getMenuId, menu.getMenuId()))) {
uniqueKey = key + "_" + suffix;
suffix++;
}
menu.setMenuKey(uniqueKey);
baseMapper.updateById(menu);
count++;
}
return count;
}
/**
* 根据菜单信息构建 menu_key
*/
private String buildMenuKey(SysMenu menu) {
// 优先使用 path
if (StringUtils.isNotBlank(menu.getPath())) {
String path = menu.getPath().trim();
// 去掉首尾 /
if (path.startsWith("/")) {
path = path.substring(1);
}
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
// 剩余 / 替换为 _非字母数字替换为 _
return path.replaceAll("[^a-zA-Z0-9_\\u4e00-\\u9fa5]", "_")
.replaceAll("_+", "_")
.replaceAll("(^_|_$)", "");
}
// path 为空时,使用 menuName 作为 key
if (StringUtils.isNotBlank(menu.getMenuName())) {
return menu.getMenuName().trim().replaceAll("[^a-zA-Z0-9_\\u4e00-\\u9fa5]", "_")
.replaceAll("_+", "_")
.replaceAll("(^_|_$)", "");
}
return null;
}
/**
* 获取路由名称
*

View File

@@ -7,6 +7,7 @@
<resultMap type="SysMenu" id="SysMenuResult">
<id property="menuId" column="menu_id"/>
<result property="menuName" column="menu_name"/>
<result property="menuKey" column="menu_key"/>
<result property="parentName" column="parent_name"/>
<result property="parentId" column="parent_id"/>
<result property="orderNum" column="order_num"/>
@@ -29,7 +30,7 @@
</resultMap>
<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query_param, m.visible, m.status,
select distinct m.menu_id, m.parent_id, m.menu_name, m.menu_key, m.path, m.component, m.query_param, m.visible, m.status,
m.perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.style, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
@@ -42,6 +43,7 @@
select distinct m.menu_id,
m.parent_id,
m.menu_name,
m.menu_key,
m.path,
m.component,
m.query_param,