背景
上篇中介绍了权限注册和权限过滤器的使用,这篇我想讲下我对菜单的设计,在面向对象的设计中有一个很重要的观点谁拥有信息谁拥有职责,秉承这个原则,那么谁拥有操作仅限就拥有这个菜单的权限了,在Vue做为后台管理大行其道的今天,应该是叫router比较合适。
注解设计
所以我在设计SystemResource这个注解时就设计了,menuCode/menuName/parentMenuCode/parentMenuName这四个feild来标注菜单,其中parentMenuCode/parentMenuName用来标注在类上的,menuCode/menuName标注在方法上。
package org.cloud.dimension.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.cloud.constant.CoreConstant;
import org.springframework.stereotype.Component;
/**
* 系统资源注解
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface SystemResource {
String path() default ""; //资源路径,用于分类资源,例如 商品管理/SPU管理
String description() default ""; // 资源名称
String value() default ""; // 资源code
CoreConstant.AuthMethod authMethod() default CoreConstant.AuthMethod.ALLSYSTEMUSER; //验证方式,默认为所有的登录用户,ByUserAuth
// 如下为菜单相关
String menuName() default ""; // 菜单名称,可选,如果不为空,那么会自动创建菜单
String menuCode() default ""; // 菜单编码,可选,如果不为空,那么会自动创建菜单
String parentMenuCode() default ""; // 父级菜单名称,默认为无上级,用controller的类上
String parentMenuName() default ""; // 父级菜单名称,默认为无上级,用controller的类上
int index() default 0; // 序号
String icon() default "";
}
资源和菜单同步
在上篇中提到同步资源的时候,首先会同步父级菜单到menu表里
private ParentMenuCreate getParentMenuCreate(Map<String, Object> beans, String beanName) {
SystemResource beanResourceAnnotation = AnnotationUtils.findAnnotation(beans.get(beanName).getClass(), SystemResource.class);
logger.info("beanResourceAnnotation:{}", beanResourceAnnotation);
TFrameMenu parentMenu = null;
if (beanResourceAnnotation == null) {
return null;
}
if (!SystemStringUtil.single().isEmpty(beanResourceAnnotation.parentMenuCode())) {
parentMenu = new TFrameMenu();
parentMenu.setMenuCode(beanResourceAnnotation.parentMenuCode());
parentMenu.setMenuName(beanResourceAnnotation.parentMenuName());
parentMenu.setSeqNo(beanResourceAnnotation.index());
parentMenu.setCreateBy("admin");
parentMenu.setCreateDate(new Date());
parentMenu.setUpdateBy("admin");
parentMenu.setUpdateDate(new Date());
parentMenu.setType(0); // 类上注解定义为目录
parentMenu.setShowType(0);
parentMenu.setIcon(StrUtil.isEmpty(beanResourceAnnotation.icon())? "iconfont icon-caidan" : beanResourceAnnotation.icon());
parentMenu.setComponentPath("layout/routerView/parent"); //新的后台管理
parentMenu.setMenuUrl(beanResourceAnnotation.path());
parentMenu.setStatus(1);
try {
String jsonStr = JSON.toJSONString(systemResourceRegisterFeignClient.saveOrUpdateMenu(parentMenu).getData());
parentMenu = JSON.parseObject(jsonStr, TFrameMenu.class);
logger.info("parentMenu:{}", parentMenu);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
return new ParentMenuCreate(beanResourceAnnotation, parentMenu);
}
然后会将方法上的menuCode注解挂在定义的父菜单下,并将菜单和资源的关系绑定在一起
TFrameMenu menu = new TFrameMenu();
menu.setMenuCode(methodResourceAnnotation.menuCode());
menu.setMenuName(methodResourceAnnotation.menuName());
menu.setSeqNo(methodResourceAnnotation.index());
menu.setCreateBy("admin");
menu.setParentMenuId(parentMenuResult.parentMenu.getMenuId());
menu.setIcon(StrUtil.isEmpty(methodResourceAnnotation.icon()) ? "iconfont icon-biaodan" : methodResourceAnnotation.icon());
menu.setType(1);
menu.setMenuUrl(StrUtil.addPrefixIfNot(methodResourceAnnotation.menuCode(),"/")); // 新版本的后台管理,加上/
menu.setComponentPath(methodResourceAnnotation.menuCode() + "/index");
menu.setCreateDate(new Date());
menu.setUpdateBy("admin");
menu.setUpdateDate(new Date());
menu.setStatus(1);
if (AuthMethod.BYUSERPERMISSION.equals(methodResourceAnnotation.authMethod())) {
menu.setShowType(1);
String functionResourceCode = String.format("%s::%s::%s", microName, parentMenuResult.beanResourceAnnotation.path(),
methodResourceAnnotation.value());
menu.setFunctionResourceCode(functionResourceCode);
} else {
menu.setShowType(0);
}
try {
logger.info("menu:{}", systemResourceRegisterFeignClient.saveOrUpdateMenu(menu));
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
菜单同步后,前台根据同步的代码路径开发相应的页面就好了。
总结
通过这篇文章,其实已经基本讲清楚的操作权限相关的内容,下面会讲操作权限的相关内容,敬请期待。详细代码请参考

被折叠的 条评论
为什么被折叠?



