基於SpringBoot的後臺管理系統(異常、註解、node、page)(二)

Guo_1_9發表於2018-03-04

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);
複製程式碼

相關文章