『博客开发日记-后台』之添加菜单接口的实现

本文最后更新于 2026年5月6日 晚上

获取添加菜单接口的实现


获取菜单表单数据接口的需求

校验父菜单是否存在(如果不是顶级菜单)

对路由路径进行校验,其中目录和菜单必填,按钮可不填

检查同一父菜单下是否存在同名菜单

检查同一父菜单下路由路径是否重复(仅检查新增目录和菜单的情况)

对 params 字段进行处理 要将其转换成 string 再存入数据库


代码实现

创建 AddMenuDto 和 MenuParamDto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* 添加菜单请求DTO
*
* @author mengze
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(description = "添加菜单请求对象")
public class AddMenuDto {

@NotNull(message = "父菜单ID不能为空")
@ApiModelProperty(value = "父菜单ID(0表示顶级菜单)", required = true, example = "0")
private Long parentId;

@NotBlank(message = "菜单名称不能为空")
@ApiModelProperty(value = "菜单名称", required = true, example = "博客管理")
private String name;

@NotBlank(message = "菜单类型不能为空")
@Pattern(regexp = "^[CMB]$", message = "菜单类型只能为C(目录)、M(菜单)或B(按钮)")
@ApiModelProperty(value = "菜单类型(C-目录 M-菜单 B-按钮)", required = true, example = "C")
private String type;

@ApiModelProperty(value = "路由路径", example = "/blog")
private String path;

@ApiModelProperty(value = "路由名称(前端路由name)", example = "Blog")
private String routeName;

@ApiModelProperty(value = "路由路径(自定义路由路径(菜单和目录必填,按钮可不填))", example = "/blog/list")
private String routePath;

@ApiModelProperty(value = "重定向路径", example = "/blog/list")
private String redirect;

@ApiModelProperty(value = "组件路径(目录填Layout,菜单填组件路径)", example = "blog/index")
private String component;

@ApiModelProperty(value = "菜单图标", example = "el-icon-document")
private String icon;

@ApiModelProperty(value = "显示排序(数字越小越靠前)", example = "1")
private Integer sort;

@Pattern(regexp = "^[01]$", message = "菜单可见状态只能为0或1")
@ApiModelProperty(value = "菜单是否可见(0-显示 1-隐藏)", required = true, example = "0")
private String visible;

@JsonDeserialize(using = BooleanToStringUtils.class)
@ApiModelProperty(value = "目录是否始终显示(0-显示 1-隐藏)", required = true, example = "0")
private String alwaysShow;

@ApiModelProperty(value = "权限标识(按钮必填)", example = "system:user:add")
private String perm;

@JsonDeserialize(using = BooleanToStringUtils.class)
@ApiModelProperty(value = "是否缓存(0-缓存 1-不缓存)", example = "0")
private String keepAlive;

@ApiModelProperty(value = "路由参数数组", example = "[{\"key\":\"from\",\"value\":\"admin\"}]")
private List<MenuParamDto> params;
}


1
2
3
4
5
6
7
8
9
10
11
12
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(description = "菜单路由参数请求对象")
public class MenuParamDto
{
@ApiModelProperty(value = "参数名称", example = "from")
private String key;

@ApiModelProperty(value = "参数值", example = "admin")
private String value;
}

在 SysMenuServiceImpl 中实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

//新增菜单
@Override
public ResponseResult addMenu(AddMenuDto addMenuDto)
{
//校验父菜单是否存在(如果不是顶级菜单)
if (addMenuDto.getParentId() != 0L) {
SysMenu parentMenu = getById(addMenuDto.getParentId());
if (parentMenu == null) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "父菜单不存在!");
}
}

//校验路由路径:目录和菜单必填,按钮可不填
if ((SystemConstants.MENU_TYPE_C.equals(addMenuDto.getType()) ||
SystemConstants.MENU_TYPE_M.equals(addMenuDto.getType())) &&
!StringUtils.hasText(addMenuDto.getRoutePath())) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "目录和菜单的路由路径不能为空!");
}

//检查同一父菜单下是否存在同名菜单
LambdaQueryWrapper<SysMenu> nameQueryWrapper = new LambdaQueryWrapper<>();
nameQueryWrapper.eq(SysMenu::getParentId, addMenuDto.getParentId())
.eq(SysMenu::getName, addMenuDto.getName());
long nameCount = count(nameQueryWrapper);

if (nameCount > 0) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "该父菜单下已存在同名菜单,请勿重复添加!");
}

//检查同一父菜单下路由路径是否重复(仅对目录和菜单类型)
if (StringUtils.hasText(addMenuDto.getPath()) &&
(SystemConstants.MENU_TYPE_C.equals(addMenuDto.getType()) ||
SystemConstants.MENU_TYPE_M.equals(addMenuDto.getType()))) {
LambdaQueryWrapper<SysMenu> pathQueryWrapper = new LambdaQueryWrapper<>();
pathQueryWrapper.eq(SysMenu::getParentId, addMenuDto.getParentId())
.eq(SysMenu::getPath, addMenuDto.getPath());
long pathCount = count(pathQueryWrapper);

if (pathCount > 0) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "该父菜单下已存在相同路由地址!");
}
}

//复制基本属性
SysMenu menu = BeanCopyUtils.copyBean(addMenuDto, SysMenu.class);

//将routePath的值同时保存到path中
if (StringUtils.hasText(updateMenuDto.getRoutePath())) {
menu.setPath(updateMenuDto.getRoutePath());
}

//处理 params 字段:List<MenuParamDto> -> JSON String
if (addMenuDto.getParams() != null && !addMenuDto.getParams().isEmpty()) {
menu.setParams(JSON.toJSONString(addMenuDto.getParams()));
}

//保存菜单
sysMenuService.save(menu);

return ResponseResult.okResult();
}




PS:该系列只做为作者学习开发项目做的笔记用

不一定符合读者来学习,仅供参考


预告

后续会记录博客的开发过程

每次学习会做一份笔记来进行发表

“一花一世界,一叶一菩提”


版权所有 © 2026 云梦泽
欢迎访问我的个人网站:https://hgt12.github.io/


『博客开发日记-后台』之添加菜单接口的实现
http://example.com/2026/05/05/『博客开发日记-后台』之添加菜单接口的实现/
作者
云梦泽
发布于
2026年5月5日
更新于
2026年5月6日
许可协议