基于SpringBoot的后台管理系统(异常、注解、node、page)(二)

common.exception、annotation、node、page

说明

如果您有幸能看到,请认阅读以下内容;

  • 1、本项目临摹自abel533的Guns,他的项目 fork 自 stylefengGuns!开源的世界真好,可以学到很多知识。
  • 2、版权归原作者所有,自己只是学习使用。跟着大佬的思路,希望自己也能变成大佬。gogogo》。。
  • 3、目前只是一个后台模块,希望自己技能增强到一定时,可以把stylefeng 的 [Guns]融合进来。
  • 4、note里面是自己的学习过程,菜鸟写的,不是大佬写的。内容都是大佬的。

异常

1、首先来看所有业务异常的枚举类,看异常大概就能知道这个系统主要完成那些业务逻辑。

/**
 * 所有业务异常的枚举
 */
public enum BizExceptionEnum {

    /**
     * 字典
     */
    DICT_EXISTED(400,"字典已经存在"),
    ERROR_CREATE_DICT(500,"创建字典失败"),
    ERROR_WRAPPER_FIELD(500,"包装字典属性失败"),

    /**
     * 文件上传
     */
    FILE_READING_ERROR(400,"FILE_READING_ERROR!"),
    FILE_NOT_FOUND(400,"FILE_NOT_FOUND!"),
    UPLOAD_ERROR(500,"上传图片出错"),

    /**
     * 权限和数据问题
     */
    DB_RESOURCE_NULL(400,"数据库中没有该资源"),
    NO_PERMITION(405, "权限异常"),
    REQUEST_INVALIDATE(400,"请求数据格式不正确"),
    INVALID_KAPTCHA(400,"验证码不正确"),
    CANT_DELETE_ADMIN(600,"不能删除超级管理员"),
    CANT_FREEZE_ADMIN(600,"不能冻结超级管理员"),
    CANT_CHANGE_ADMIN(600,"不能修改超级管理员角色"),

    /**
     * 账户问题
     */
    USER_ALREADY_REG(401,"该用户已经注册"),
    NO_THIS_USER(400,"没有此用户"),
    USER_NOT_EXISTED(400, "没有此用户"),
    ACCOUNT_FREEZED(401, "账号被冻结"),
    OLD_PWD_NOT_RIGHT(402, "原密码不正确"),
    TWO_PWD_NOT_MATCH(405, "两次输入密码不一致"),

    /**
     * 错误的请求
     */
    MENU_PCODE_COINCIDENCE(400,"菜单编号和副编号不能一致"),
    EXISTED_THE_MENU(400,"菜单编号重复,不能添加"),
    DICT_MUST_BE_NUMBER(400,"字典的值必须为数字"),
    REQUEST_NULL(400, "请求有错误"),
    SESSION_TIMEOUT(400, "会话超时"),
    SERVER_ERROR(500, "服务器异常");

    BizExceptionEnum(int code, String message) {
        this.friendlyCode = code;
        this.friendlyMsg = message;
    }

    private int friendlyCode;
    private String friendlyMsg;
    private String urlPath;
    //Setter,Getter,Constractor略
    BizExceptionEnum(int code, String message) {
        this.friendlyCode = code;
        this.friendlyMsg = message;
    }
}

2、对业务异常的封装,首先需要注意的是继承自RuntimeException,之前讲过了点这里

/**
 * 业务异常的封装
 */
public class BussinessException extends RuntimeException {

    //友好提示的code码
    private int friendlyCode;

    //友好提示
    private String friendlyMsg;

    //业务异常调整页面
    private String urlPath;

    public BussinessException(BizExceptionEnum bizExceptionEnum) {
        this.friendlyCode = bizExceptionEnum.getCode();
        this.friendlyMsg = bizExceptionEnum.getMessage();
        this.urlPath = bizExceptionEnum.getUrlPath();
    }
}

3、接下来是工具类初始化异常,需要注意serialVersionUID的作用:

  • 1、serialVersionUID 是 Java 为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。
  • 2、如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。
  • 3、序列化的类可显式声明 serialVersionUID 的值,

这个中定义异常和PayMap中定义方式几乎一样。

/**
 * 工具类初始化
 */
public class ToolBoxException extends RuntimeException {
    //serialVersionUID 是 Java 为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。
    // 如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。序列化的类可显式声明 serialVersionUID 的值,
    private static final long serialVersionUID = 8247610319171014183L;

    public ToolBoxException(Throwable e) {
        super(e.getMessage(),e);
    }

    public ToolBoxException(String message) {
        super(message);
    }

    public ToolBoxException(String message, Throwable throwable) {
        super(message,throwable);
    }
    public ToolBoxException(String messageTemplate, Object...params) {
        super(StrKit.format(messageTemplate,params));
    }
}
--------------------------------------------------------------------------------
/**
 * 验证码错误异常
 *
 * @Author guo             //这个模板不错
 * @Date 2018-03-04 12:04.
 */
public class InvalidKaptchaException extends RuntimeException {
}

注解

4、最后在看下自定义注解

元注解:

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用提供对其他annotation类型的说明。

  • 1、@Target
  • 2、@Retention
  • 3、@Documented
  • 4、@Inherited


@Target

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

取值(ElementType)有:

  • 1.CONSTRUCTOR:用于描述构造器
  • 2.FIELD:用于描述域
  • 3.LOCAL_VARIABLE:用于描述局部变量
  • 4.METHOD:用于描述方法
  • 5.PACKAGE:用于描述包
  • 6.PARAMETER:用于描述参数
  • 7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

@Retention

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

取值(RetentionPoicy)有:

  • 1.SOURCE:在源文件中有效(即源文件保留)
  • 2.CLASS:在class文件中有效(即class保留)
  • 3.RUNTIME:在运行时有效(即运行时保留)

@Documented

作用:用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

@Inherited

@Inherited元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

接下来,我们在看自定义注解

/**
 * 权限注解,用于检查权限 规定访问权限
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)   //运行时有效
@Target({ElementType.METHOD})         //方法范围
public @interface Permission {
    String[] value() default {};
}
-------------------------------------------------------
/**
 * 多数据源标识
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface DataSource {
}
---------------------------------------------------------
/**
 * 标记需要做业务日志的方法
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface BussinessLog {

    /**
     * 业务的名称,例如:"修改菜单"
     */
    String value() default "";

    /**
     * 被修改的实体的唯一标识,例如:菜单实体的唯一标识为"id"
     */
    String key() default "id";

    /**
     * 字典(用于查找key的中文名称和字段的中文名称)
     */
    String dict() default "SystemDict";
}


5、先来看Node类的定义吧,

ZTree is an advanced jQuery ‘tree plug-in’. The performance is excellent, it is easy to configurw (with a full set of options), and has many advanced features (that usually only come with paid software).

zTree is open source and uses the MIT license.

  • Supports JSON data.
  • Supports both static and asynchronous (Ajax) data loading.
  • Supports multiple instances of zTree in one page.
  • zTree3.0 can use lazy loading for large data, it can easily load tens of thousands of nodes in seconds even in the IE6 browser.

The most important is the official document to the very full(English is very important, important and important.)

ZTreeNode定义:

/**
 * jquery ztree 插件的节点
 */
public class ZTreeNode {
    /**
     * 节点id
     */
    private Integer id;
    /**
     * 父节点id
     */
    private Integer pId;
    /**
     * 节点名称
     */
    private String name;
    /**
     * 是否打开节点
     */
    private Boolean open;
    /**
     * 是否被选中
     */
    private Boolean checked;
    //Setter、Getter、Constructor、toString忽略
}

MenuNode实现了Compareable接口,重写了compareTo()方法完整代码在这


@Override
public int compareTo(Object o) {
    MenuNode menuNode = (MenuNode) o;
    Integer num = menuNode.getNum();
    if (num == null) {
        num = 0;
    }
    return this.num.compareTo(num);
}
/**
 *
 * 菜单的节点
 */
public class MenuNode implements Comparable {
    /**
     * 节点id
     */
    private Integer id;
    /**
     * 父节点
     */
    private Integer parentId;
    /**
     * 节点名称
     */
    private String name;
    /**
     * 按钮级别
     */
    private Integer levels;
    /**
     * 按钮级别
     */
    private Integer ismenu;
    /**
     * 按钮的排序
     */
    private Integer num;
    /**
     * 节点的url
     */
    private String url;
    /**
     * 节点图标
     */
    private String icon;
    /**
     * 子节点的集合
     */
    private List<MenuNode> children;
    /**
     * 查询子节点时候的临时集合
     */
    private List<MenuNode> linkedList = new ArrayList<MenuNode>();
}

为了方便以后查看,方法单独提出来。

    /**
     * 构建整个菜单树
     */
    public void buildNodeTree(List<MenuNode> nodeList) {
        for (MenuNode treeNode : nodeList) {
            List<MenuNode> linkedList = treeNode.findChildNodes(nodeList, treeNode.getId());
            if (linkedList.size() > 0) {
                treeNode.setChildren(linkedList);
            }
        }
    }
    /**
     * 查询子节点的集合
     */
    public List<MenuNode> findChildNodes(List<MenuNode> nodeList, Integer parentId) {
        if (nodeList == null && parentId == null)
            return null;
        for (Iterator<MenuNode> iterator = nodeList.iterator(); iterator.hasNext(); ) {
            MenuNode node = (MenuNode) iterator.next();
            // 根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (node.getParentId() != 0 && parentId.equals(node.getParentId())) {
                recursionFn(nodeList, node, parentId);
            }
        }
        return linkedList;
    }
--------------------------------------------------------------------------------
    /**
     * 遍历一个节点的子节点
     */
    public void recursionFn(List<MenuNode> nodeList, MenuNode node, Integer pId) {
        List<MenuNode> childList = getChildList(nodeList, node);// 得到子节点列表
        if (childList.size() > 0) {// 判断是否有子节点
            if (node.getParentId().equals(pId)) {
                linkedList.add(node);
            }
            Iterator<MenuNode> it = childList.iterator();
            while (it.hasNext()) {
                MenuNode n = (MenuNode) it.next();
                recursionFn(nodeList, n, pId);
            }
        } else {
            if (node.getParentId().equals(pId)) {
                linkedList.add(node);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<MenuNode> getChildList(List<MenuNode> list, MenuNode node) {
        List<MenuNode> nodeList = new ArrayList<MenuNode>();
        Iterator<MenuNode> it = list.iterator();
        while (it.hasNext()) {
            MenuNode n = (MenuNode) it.next();
            if (n.getParentId().equals(node.getId())) {
                nodeList.add(n);
            }
        }
        return nodeList;
    }
--------------------------------------------------------------------------------
    /**
     * 清除掉按钮级别的资源
     *
     * @param nodes
     * @return
     */
    public static List<MenuNode> clearBtn(List<MenuNode> nodes) {
        ArrayList<MenuNode> noBtns = new ArrayList<MenuNode>();
        for (MenuNode node : nodes) {
            if(node.getIsmenu() == IsMenu.YES.getCode()){
                noBtns.add(node);
            }
        }
        return noBtns;
    }

    /**
     * 清除所有二级菜单
     *
     * @param nodes
     * @return
     */
    public static List<MenuNode> clearLevelTwo(List<MenuNode> nodes) {
        ArrayList<MenuNode> results = new ArrayList<MenuNode>();
        for (MenuNode node : nodes) {
            Integer levels = node.getLevels();
            if (levels.equals(1)) {
                results.add(node);
            }
        }
        return results;
    }
--------------------------------------------------------------------------------
    /**
     * 构建菜单列表
     */
    public static List<MenuNode> buildTitle(List<MenuNode> nodes) {

        List<MenuNode> clearBtn = clearBtn(nodes);

        new MenuNode().buildNodeTree(clearBtn);

        List<MenuNode> menuNodes = clearLevelTwo(clearBtn);

        //对菜单排序
        Collections.sort(menuNodes);

        //对菜单的子菜单进行排序
        for (MenuNode menuNode : menuNodes) {
            if (menuNode.getChildren() != null && menuNode.getChildren().size() > 0) {
                Collections.sort(menuNode.getChildren());
            }
        }

        //如果关闭了接口文档,则不显示接口文档菜单
        GunsProperties gunsProperties = SpringContextHolder.getBean(GunsProperties.class);
        if (!gunsProperties.getSwaggerOpen()) {
            List<MenuNode> menuNodesCopy = new ArrayList<>();
            for (MenuNode menuNode : menuNodes) {
                if (Const.API_MENU_NAME.equals(menuNode.getName())) {
                    continue;
                } else {
                    menuNodesCopy.add(menuNode);
                }
            }
            menuNodes = menuNodesCopy;
        }

        return menuNodes;
    }

zTree简单使用

获取所有的选择节点、获取子节点

// 通过id号来获取这个树
var treeObj2 = $.fn.zTree.getZTreeObj("treeDemo");
// 获取所有已经选择了的节点,获得一个node列表
var nodes2 = treeObj2.getCheckedNodes(true);
// 如果是叶子节点就把id拿出来
var idlist = [];
$.each(nodes2, function (i, item) {
    if (!item.isParent) {
        //alert(item.id + ","  + item.name);
        idlist.push(item.id);
    }
});
console.log(idlist);

6、接下来,我们看看pagehelper,Mybatis通用分页插件这个插件也是本项目大佬写的。如果非要自己封装也可以,但是你用过的话就不会在自己封装了。主要是PageInfo类。

package com.guo.guns.common.page;

import com.github.pagehelper.Page;

import java.util.List;

/**
 * 分页结果的封装(for Bootstrap Table)
 *
 * @Author guo
 * @Date 2018-03-04 13:47
 */
public class PageInfoBT<T> {

    /**
     *  结果集
     */
    private List<T> rows;

    /**
     * 总数
     */
    private long total;

    public PageInfoBT(List<T> page) {
        this.rows = page;
        if (page instanceof Page) {
            this.total = ((Page) page).getTotal();
        } else {
            this.total = page.size();
        }
    }
  //Setter、Getter略
}

PageHelper简单使用

(1)1、查出第一页的数据,每页5条:

 PageHelper.offsetPage(0, 5);

(2)、获取总数:

PageInfo pageInfo = new PageInfo<>(cs);
System.out.println("总数:"+pageInfo.getTotal());
System.out.println(pageInfo);
    原文作者:guoxiaoxu
    原文地址: https://segmentfault.com/a/1190000013509759
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞