feat(menu): 添加菜单唯一标识功能
- 新增 menuKey 字段到 SysMenu 实体类 - 在菜单服务接口和服务实现中添加 checkMenuKeyUnique、selectMenuByMenuKey 和 generateMenuKeys 方法 - 在控制器中增加 getByKey 和 generateMenuKeys 接口用于通过唯一标识获取菜单和批量生成菜单标识 - 在数据库映射中添加对 menuKey 字段的支持 - 在新增和修改菜单时验证 menuKey 唯一性 - 实现自动生成菜单唯一标识的功能,支持基于路径或菜单名称生成
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路由名称
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user