资讯专栏INFORMATION COLUMN

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

paulli3 / 2257人阅读

摘要:序列化的类可显式声明的值,这个中定义异常和中定义方式几乎一样。工具类初始化是为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。

common.exception、annotation、node、page 说明

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

1、本项目临摹自abel533的Guns,他的项目 fork 自 stylefeng 的 Guns!开源的世界真好,可以学到很多知识。

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 children;
    /**
     * 查询子节点时候的临时集合
     */
    private List linkedList = new ArrayList();
}

为了方便以后查看,方法多带带提出来。

    /**
     * 构建整个菜单树
     */
    public void buildNodeTree(List nodeList) {
        for (MenuNode treeNode : nodeList) {
            List linkedList = treeNode.findChildNodes(nodeList, treeNode.getId());
            if (linkedList.size() > 0) {
                treeNode.setChildren(linkedList);
            }
        }
    }
    /**
     * 查询子节点的集合
     */
    public List findChildNodes(List nodeList, Integer parentId) {
        if (nodeList == null && parentId == null)
            return null;
        for (Iterator 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 nodeList, MenuNode node, Integer pId) {
        List childList = getChildList(nodeList, node);// 得到子节点列表
        if (childList.size() > 0) {// 判断是否有子节点
            if (node.getParentId().equals(pId)) {
                linkedList.add(node);
            }
            Iterator 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 getChildList(List list, MenuNode node) {
        List nodeList = new ArrayList();
        Iterator 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 clearBtn(List nodes) {
        ArrayList noBtns = new ArrayList();
        for (MenuNode node : nodes) {
            if(node.getIsmenu() == IsMenu.YES.getCode()){
                noBtns.add(node);
            }
        }
        return noBtns;
    }

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

        List clearBtn = clearBtn(nodes);

        new MenuNode().buildNodeTree(clearBtn);

        List 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 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 {

    /**
     *  结果集
     */
    private List rows;

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

    public PageInfoBT(List 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);

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68651.html

相关文章

  • 基于SpringBoot后台管理系统(Encache配置、全局异常处理(重点))(四)

    摘要:因为抽象类天生就是用来被继承的。由于不支多继承,子类不能够继承多个类,但可以实现多个接口如果基本功能在不断改变,那么就需要使用抽象类。全局异常处理接下来,我们在看看控制统一的异常拦截机制。 3、Spring Boot 缓存配置、全局异常处理 说明 如果您有幸能看到,请认阅读以下内容; 1、本项目临摹自abel533的Guns,他的项目 fork 自 stylefeng 的 Guns!...

    Benedict Evans 评论0 收藏0
  • 基于SpringBoot后台管理系统(启动类解析,开源世界真好)(一)

    摘要:目前只是一个后台模块,希望自己技能增强到一定时,可以把的融合进来。目录第一站,分析了启动类。看见没,这个也是配置类,它声明了视图解析器地域解析器以及静态资源的位置,想起来没,就是前置,后置。程序启动类我们点击源码看看。 Guns基于SpringBoot,致力于做更简洁的后台管理系统,完美整合springmvc + shiro + 分页插件PageHelper + 通用Mapper + ...

    SwordFly 评论0 收藏0
  • 基于 SpringBoot2.0+优雅整合 SpringBoot+Mybatis

    摘要:基于最新的,是你学习的最佳指南。驱动程序通过自动注册,手动加载类通常是不必要。由于加上了注解,如果转账中途出了意外和的钱都不会改变。三的方式项目结构相比于注解的方式主要有以下几点改变,非常容易实现。公众号多篇文章被各大技术社区转载。 Github 地址:https://github.com/Snailclimb/springboot-integration-examples(Sprin...

    gghyoo 评论0 收藏0
  • SpringBoot中并发定时任务实现、动态定时任务实现(看这一篇就够了)

    摘要:也是自带的一个基于线程池设计的定时任务类。其每个调度任务都会分配到线程池中的一个线程执行,所以其任务是并发执行的,互不影响。 原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10659045.html,否则将追究法律责任!!! 一、在JAVA开发领域,目前可以通过以下几种方式进行定时任务 1、单机部署模式 Timer:jdk中...

    BWrong 评论0 收藏0
  • SpringBoot 实战 (十五) | 服务端参数校验之一

    摘要:前言估计很多朋友都认为参数校验是客户端的职责,不关服务端的事。轻则导致服务器宕机,重则泄露数据。所以,这时就需要设置第二道关卡,服务端验证了。老项目的服务端校验不能为空不能为空看以上代码,就一个的校验就如此麻烦。 前言 估计很多朋友都认为参数校验是客户端的职责,不关服务端的事。其实这是错误的,学过 Web 安全的都知道,客户端的验证只是第一道关卡。它的参数验证并不是安全的,一旦被有心人...

    QiShare 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<