黑馬頭條項目[修改版]-第一天


0.黑馬頭條-學習目標

學習目標

  • 能夠描述黑馬頭條項目有哪些業務
  • 能夠了解黑馬頭條項目使用到什么技術
  • 能夠了解黑馬頭條數據庫設計過程
  • 能夠掌握初始化工程的搭建
  • 能夠掌握接口開發的流程
  • 能夠完成頻道管理的功能開發
  • 能夠掌握使用接口swagger、postman、knife4j

今日重點

  • 能夠了解黑馬頭條數據庫設計過程
  • 能夠掌握初始化工程的搭建
  • 能夠掌握接口開發的流程
  • 能夠完成頻道管理的功能開發
  • 能夠掌握使用接口swagger、postman、knife4j

1.項目介紹

1.1.背景及概述

1.1.1.背景

隨着智能手機的普及,人們更加習慣於通過手機來看新聞。由於生活節奏的加快,很多人只能利用碎片時間來獲取信息,因此,對於移動資訊客戶端的需求也越來越高。黑馬頭條項目正是在這樣背景下開發出來。黑馬頭條項目采用當下火熱的微服務+大數據技術架構實現。本項目主要着手於獲取最新最熱新聞資訊,通過大數據分析用戶喜好精確推送咨詢新聞。

1.1.2.概述

黑馬頭條項目是對在線教育平台業務進行大數據統計分析的系統。碎片化、切換頻繁、社交化和個性化現如今成為人們閱讀行為的標簽。黑馬頭條對海量信息進行搜集,通過系統計算分類,分析用戶的興趣進行推送從而滿足用戶的需求。

1.1.3.項目術語定義

  • 項目:泛指黑馬頭條整個項目或某一項目模塊
  • 工程:泛指黑馬頭條某一項目的源碼工程
  • 用戶:泛指黑馬頭條APP用戶端用戶
  • 自媒體人:泛指通過黑馬自媒體系統發送文章的用戶
  • 管理員:泛指黑馬頭條管理系統的使用用戶
  • App:泛指黑馬頭條APP
  • WeMedia:泛指黑馬頭條自媒體系統
  • Admin:泛指黑馬頭條管理系統

1.2.功能需求說明

1.2.1.功能架構圖

1.2.2.App主要功能大綱

  • 頻道欄:用戶可以通過此功能添加自己感興趣的頻道,在添加標簽時,系統可依據用戶喜好進行推薦
  • 文章列表:需要顯示文章標題、文章圖片、評論數等信息,且需要監控文章是否在APP端展現的行為
  • 搜索文章:聯想用戶想搜索的內容,並記錄用戶的歷史搜索信息
  • 個人中心:用戶可以在其個人中心查看收藏、關注的人、以及系統設置等功能
  • 查看文章:用戶點擊文章進入查看文章頁面,在此頁面上可進行點贊、評論、不喜歡、分享等操作;除此之外還需要收集用戶查看文章的時間,是否看我等行為信息
  • 實名認證:用戶可以進行身份證認證和實名認證,實名認證之后即可成為自媒體人,在平台上發布文章
  • 注冊登錄:登錄時,驗證內容為手機號登錄/注冊,通過手機號驗證碼進行登錄/注冊,首次登錄用戶自動注冊賬號。

1.2.3.自媒體端功能大綱

  • 內容管理:自媒體用戶管理文章頁面,可以根據條件進行篩選,文章包含草稿、已發布、未通過、已撤回狀態。用戶可以對文章進行修改,上/下架操作、查看文章狀態等操作
  • 評論管理:管理文章評論頁面,顯示用戶已發布的全部文章,可以查看文章總評論數和粉絲評論數,可以對文章進行關閉評論等操作
  • 素材管理:管理自媒體文章發布的圖片,便於用戶發布帶有多張圖片的文章
  • 圖文數據:自媒體人發布文章的數據:閱讀數、評論數、收藏了、轉發量,用戶可以查看對應文章的閱讀數據
  • 粉絲畫像:內容包括:粉絲性別分布、粉絲年齡分布、粉絲終端分布、粉絲喜歡分類分布

1.2.4.平台管理端功能大綱

  • 用戶管理:系統后台用來維護用戶信息,可以對用戶進行增刪改查操作,對於違規用戶可以進行凍結操
  • 用戶審核:管理員審核用戶信息頁面,用戶審核分為身份審核和實名審核,身份審核是對用戶的身份信息進行審核,包括但不限於工作信息、資質信息、經歷信息等;實名認證是對用戶實名身份進行認證
  • 內容管理:管理員查詢現有文章,並對文章進行新增、刪除、修改、置頂等操作
  • 內容審核:管理員審核自媒體人發布的內容,包括但不限於文章文字、圖片、敏感信息等
  • 頻道管理:管理頻道分類界面,可以新增頻道,查看頻道,新增或修改頻道關聯的標簽
  • 網站統計:統計內容包括:日活用戶、訪問量、新增用戶、訪問量趨勢、熱門搜索、用戶地區分布等數據
  • 內容統計:統計內容包括:文章采集量、發布量、閱讀量、閱讀時間、評論量、轉發量、圖片量等數據
  • 權限管理:超級管理員對后台管理員賬號進行新增或刪除角色操作

1.2.5.其他需求

1.2.6.交互需求

1.3.技術架構說明

包括前端(Weex、Vue、Echarts、WS)、網關(GateWay)、DevOps(單元測試、代碼規范)

服務層中包括中間件(Kafka)、索引、微服務、大數據存儲等重難點技術

  • Weex+Vue+WebSocket :使用Weex跨平台開發工具,整合集成VUE框架,完成黑馬頭條移動端功能開發,並集成WebSocket實現即時消息(文章推薦、私信)的推送
  • Vue+Echarts : 自媒體系統使用Vue開發關鍵,集成Echarts圖表框架,完成相關粉絲畫像、數據分析等功能
  • Vue+Echarts+WebSocket : 管理系統也是使用Vue開發,集成Echarts,完成網站統計、內容統計等功能,集成WebSocket,實現系統看板實時數據自動化更新
  • Spring-Cloud-Gateway : 微服務之前架設的網關服務,實現服務注冊中的API請求路由,以及控制流速控制和熔斷處理都是常用的架構手段,而這些功能Gateway天然支持
  • PMD&P3C : 靜態代碼掃描工具,在項目中掃描項目代碼,檢查異常點、優化點、代碼規范等,為開發團隊提供規范統一,提升項目代碼質量
  • Junit : 在持續集成思想中,單元測試偏向自動化過程,項目通過Junit+Maven的集成實現這種過程
  • 運用Spring Boot快速開發框架,構建項目工程;並結合Spring Cloud全家桶技術,實現后端個人中心、自媒體、管理中心等微服務。
  • 運用WebMagic爬蟲技術,完善系統內容自動化采集
  • 運用Kafka完成內部系統消息通知;與客戶端系統消息通知;以及實時數據計算
  • 運用MyCat數據庫中間件計算,對系統數據進行分開分表,提升系統數據層性能
  • 運用Redis緩存技術,實現熱數據的計算,NoSession等功能,提升系統性能指標
  • 運用Zoookeeper技術,完成大數據節點之后的協調與管理,提升系統存儲層高可用
  • 使用Mysql存儲用戶數據,以保證上層數據查詢的高性能
  • 使用Mongo存儲用戶熱數據,以保證用戶熱數據高擴展和高性能指標
  • 使用FastDFS作為靜態資源存儲器,在其上實現熱靜態資源緩存、淘汰等功能
  • 運用Habse技術,存儲系統中的冷數據,保證系統數據的可靠性
  • 運用ES搜索技術,對冷數據、文章數據建立索引,以保證冷數據、文章查詢性能
  • 運用Sqoop、Kettle等工具,實現大數據的離線入倉;或者數據備份到Hadoop
  • 運用Spark+Hive進行離線數據分析,實現系統中各類統計報表
  • 運用Spark Streaming + Hive+Kafka實現實時數據分析與應用;比如文章推薦
  • 運用Neo4j知識圖譜技術,分析數據關系,產出知識結果,並應用到上層業務中,以幫助用戶、自媒體、運營效果/能力提升。比如粉絲等級計算
  • 運用AI技術,來完成系統自動化功能,以提升效率及節省成本。比如實名認證自動化

2.環境搭建

2.1.數據庫設計規范及導入數據庫

2.1.1.數據庫設計

2.1.1.1.ER圖設計

er圖設計划分出了9個庫,各個庫主要解決的是某一個特定的業務。

數據庫設計規范,詳見資料文件夾下《黑馬頭條-數據庫規范設計說明書.md》文件。

PowerDesinger工具使用,詳見資料文件夾下’powerdesinger的基本使用’文件夾里的《powerdesinger的基本使用》文件。

2.1.1.2.分庫設計

黑馬頭條項目采用的分庫分表設計,因為業務比較復雜,后期的訪問量巨大,為了分攤數據庫的壓力,整個項目用的不只是一個數據庫。其中核心庫有7個,每一個數據庫解決的是一個業務點,非常接近與實際項目設計。

創建數據庫及表,可以參考資料中的sql腳本。

2.1.1.3.核心數據流轉圖

說明:整個項目其核心數據為文章信息,上圖主要說明文章的數據流轉

1 爬蟲系統從外網采集數據后入爬蟲庫,即爬蟲庫中保存了一份采集的文章信息。

2 自媒體人可以通過發布文章后首先進入自媒體庫

3 爬蟲文章和自媒體文章最后都要經過審核成功后入appinfo庫,這里面的文章信息,最終是要給app端用戶所查看。

4 在app端用戶查看的時候,需要記錄用戶的一些行為,如轉發、評論、點贊等需要入用戶行為庫

2.1.1.4.冗余設計

黑馬頭條項目全部采用邏輯關聯,沒有采用主外鍵約束。也是方便數據源冗余,盡可能少的使用多表關聯查詢。冗余是為了效率,減少join。單表查詢比關聯查詢速度要快。某個訪問頻繁的字段可以冗余存放在兩張表里,不用關聯了。

如查詢一個訂單表需要查詢該條訂單的用戶名稱,就必須join另外用戶表,如果業務表很大,那么就會查詢的很慢,這個時候我們就可以使用冗余來解決這個問題,在新建訂單的同時不僅僅需要把用戶ID存儲,同時也需要存儲用戶的名稱,這樣我們在查詢訂單表的時候就不需要去join另外用戶表,也能查詢出該條訂單的用戶名稱。這樣的冗余可以直接的提高查詢效率,單表更快。

2.2.初始化工程導入及環境說明

2.2.1.主體結構說明

后端工程基於Spring-boot 2.1.5.RELEASE 版本構建,工程父項目為heima-leadnews,並通過繼承方式集成Spring-boot

【父項目下分4個公共子項目】:

  • heima-leadnews-common : 是整個工程的配置核心,包括所有集成三方框架的配置定義,比如redis、kafka等。除此之外還包括項目每個模塊及整個項目的常量定義;
  • heima-leadnews-model :項目中用到的Dto、Pojo、Mapper、Enums定義工程;
  • heima-leadnews-utils : 工程公用工具類項目,包含加密/解密、Date、JSON等工具類;
  • heima-leadnew-apis : 整個項目微服務暴露的接口的定義項目,按每個模塊進行子包拆分;

【多個微服務】:

  • heima-leadnews-login:用於實現APP+自媒體端用戶的登錄與注冊功能;
  • heima-leadnews-user:用於實現APP端用戶中心的功能,比如我的收藏、我的粉絲等功能;
  • heima-leadnews-article:用於實現APP端文章的獲取與搜索等功能;還包括頻道、標簽等功能;
  • heima-leadnews-behavior:用於實現APP端各類行為數據的上傳服務;
  • heima-leadnews-quartz:用於封裝項目中所有的調度計算任務;
  • heima-leadnews-wemedia:用於實現自媒體管理端的功能;
  • heima-leadnews-admin:用於實現后台管理系統的功能;
  • heima-leadnews-gateway:網關

2.2.2.后端通用工程搭建

2.2.2.1.開發環境說明

項目依賴環境(需提前安裝好):

  • JDK1.8
  • Intellij Idea
  • Tomcat 8.5
  • Git

2.2.2.2.IDEA開發工具配置

1.設置本地倉庫,建議使用資料中提供好的倉庫

2.設置項目編碼格式

3.后端初始項目導入

在今天資料目錄的初始工程目錄找到heima-leadnews.zip文件,拷貝到一個沒有中文和空格的目錄,解壓縮,使用idea打開即可。

注意

資料中提供的初始項目,有問題,建議使用隨堂筆記中的初始項目。

擴展

導入項目之后,在父工程的pom.xml文件中會有一些依賴報紅(依賴沒有下載引起的),這個時候可以將報紅的依賴導入的 中,進行下載,但是xxl-job-core依賴會下載可能會下載失敗(這是因為aliyun倉庫可能沒有這個依賴),這時可以講隨堂筆記中的依賴,拷貝到本地倉庫的com目錄下

此外還有可能maven-resources-plugin也可能會報紅,可以改為以下的配置

<plugin>
   <artifactId>maven-resources-plugin</artifactId>
   <groupId>org.apache.maven.plugins</groupId>
   <version>3.1.0</version>
   <configuration>
       <useDefaultDelimiters>true</useDefaultDelimiters>
   </configuration>
</plugin>

3.開發規范

3.1.前后端分離開發

3.1.1.前后端分離開發介紹

項目基於前后端分離的架構進行開發,前后端分離架構總體上包括前端和服務端,通常是多人協作開發

  • 對於后端java工程師:

把精力放在設計模式,spring+springmvc,linux,mysql事務隔離與鎖機制,mongodb,http/tcp,多線程,分布式架構,彈性計算架構,微服務架構,java性能優化,以及相關的項目管理等等。

  • 對於前端工程師:

把精力放在html5,css3,vuejs,webpack,nodejs,Google V8引擎,javascript多線程,模塊化,面向切面編程,設計模式,瀏覽器兼容性,性能優化等等。

3.1.2.前后端分離開發流程

1,需求分析

梳理用戶的需求,分析業務流程

2,接口定義

根據需求分析定義接口,定義出接口文檔,具體寫法可以參考資料中的接口文檔目錄中的《接口示例文檔.md》

注意:controller的訪問路徑又稱之為接口,所以接口文檔是針對controller訪問路徑的描述。

3,服務端和前端並行開發

服務端:依據接口文檔進行服務端接口開發

前端:根據用戶需求開發操作界面,並根據接口文檔制作mock數據,進行測試

4,前后端集成接口聯調

最終前端調用服務端接口完成業務

3.2.后端開發通用規范

3.2.1.開發原則

  • 自頂向下的設計原則:功能應該從表現層分析再到控制層、服務層、持久層逐層設計

  • 自底向上的開發原則:上層需調用下層,因此開發應從底層向上層逐層開發項目中開發的層次次序參考DB->中間件->持久層->服務層->控制層

  • 單一職責的開發原則:類或者方法提供的功能應該單一明確,特別越底層越應單一職責,以便維護項目中Mapper方法必須功能單一,參數明確,拒絕兩種以上的持久邏輯使用同一個Mapper方法

  • 依賴倒置的開發原則:上層依賴下層,是依賴下層接口,並不是依賴下層的實現項目中每層都是通過接口調用Controller->Service->Mapper

3.2.2.開發步驟

  • 明確類定義:明確哪些是重用類,哪些是需要新增的類
  • 明確主鍵規則:確認操作表的ID生成規則,自增或id_work
  • ControllerApi定義:定義接口
  • Mapper實現:使用mybatis-plus封裝的方法還是自定義mapper映射
  • Service實現:可用通過時序圖幫助我們梳理實現邏輯
  • Controller實現:簡單的Service層調用
  • 單元測試或接口測試或前端直接聯調測試

3.2.3.接口版本規范說明

隨着業務的復雜,同一個接口可能出現多個版本,為了方便后期切換和AB測試,需要定義接口的版本號

  • 在某一個微服務下訪問controller的時候在包名下加一個版本號,如下
com.heima.article.controller.v1
  • 在訪問具體的接口方法的url映射的時候也應該加上版本說明,如下:
@RequestMapping("/api/v1/article")

擴展

AB測試:為同一個目標,設計兩種方案,將兩種方案隨機投放市場中,讓組成成分相同(相似)用戶去隨機體驗兩種方案之一,根據觀測結果,判斷哪個方案效果更好。

3.3.接口通用請求和響應

dto(Data Transfer Object):數據傳輸對象,用於展示層與服務層之間的數據傳輸對象,比如Result

3.3.1.通用的響應對象

不分頁:com.heima.model.common.dtos.ResponseResult

/**
 * 通用的結果返回類
 * @param <T>
 */
public class ResponseResult<T> implements Serializable {

    private String host;

    private Integer code;

    private String errorMessage;

    private T data;

    public ResponseResult() {
        this.code = 200;
    }

    public ResponseResult(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg, T data) {
        this.code = code;
        this.errorMessage = msg;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.errorMessage = msg;
    }

    public static ResponseResult errorResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.error(code, msg);
    }

    public static ResponseResult okResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.ok(code, null, msg);
    }

    public static ResponseResult okResult(Object data) {
        ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS,AppHttpCodeEnum.SUCCESS.getErrorMessage());
        if(data!=null) {
            result.setData(data);
        }
        return result;
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums){
        return setAppHttpCodeEnum(enums,enums.getErrorMessage());
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums,String errorMessage){
        return setAppHttpCodeEnum(enums,errorMessage);
    }

    public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
        return okResult(enums.getCode(),enums.getErrorMessage());
    }

    private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums,String errorMessage){
        return okResult(enums.getCode(),errorMessage);
    }

    public ResponseResult<?> error(Integer code, String msg) {
        this.code = code;
        this.errorMessage = msg;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data) {
        this.code = code;
        this.data = data;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.errorMessage = msg;
        return this;
    }

    public ResponseResult<?> ok(T data) {
        this.data = data;
        return this;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }
}

分頁通用返回:com.heima.model.common.dtos.PageResponseResult

public class PageResponseResult extends ResponseResult {
    private Integer currentPage;
    private Integer size;
    private Integer total;

    public PageResponseResult(Integer currentPage, Integer size, Integer total) {
        this.currentPage = currentPage;
        this.size = size;
        this.total = total;
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }
}

3.3.2.通用的請求dtos

接受頁面分頁請求參數:com.heima.model.common.dtos.PageRequestDto

@Data
@Slf4j
public class PageRequestDto {

    protected Integer size;
    protected Integer page;

    public void checkParam() {
        if (this.page == null || this.page < 0) {
            setPage(1);
        }
        if (this.size == null || this.size < 0 || this.size > 100) {
            setSize(10);
        }
    }
}

3.3.3.通用的異常枚舉

通用異常信息返回枚舉:com.heima.model.common.enums.AppHttpCodeEnum

public enum AppHttpCodeEnum {

    // 成功段0
    SUCCESS(0,"操作成功"),
    // 登錄段1~50
    NEED_LOGIN(1,"需要登錄后操作"),
    LOGIN_PASSWORD_ERROR(2,"密碼錯誤"),
    // TOKEN50~100
    TOKEN_INVALID(50,"無效的TOKEN"),
    TOKEN_EXPIRE(51,"TOKEN已過期"),
    TOKEN_REQUIRE(52,"TOKEN是必須的"),
    // SIGN驗簽 100~120
    SIGN_INVALID(100,"無效的SIGN"),
    SIG_TIMEOUT(101,"SIGN已過期"),
    // 參數錯誤 500~1000
    PARAM_REQUIRE(500,"缺少參數"),
    PARAM_INVALID(501,"無效參數"),
    PARAM_IMAGE_FORMAT_ERROR(502,"圖片格式有誤"),
    SERVER_ERROR(503,"服務器內部錯誤"),
    // 數據錯誤 1000~2000
    DATA_EXIST(1000,"數據已經存在"),
    AP_USER_DATA_NOT_EXIST(1001,"ApUser數據不存在"),
    DATA_NOT_EXIST(1002,"數據不存在"),
    // 數據錯誤 3000~3500
    NO_OPERATOR_AUTH(3000,"無權限操作");

    int code;
    String errorMessage;

    AppHttpCodeEnum(int code, String errorMessage){
        this.code = code;
        this.errorMessage = errorMessage;
    }

    public int getCode() {
        return code;
    }

    public String getErrorMessage() {
        return errorMessage;
    }
}

3.3.4.使用示例

在ResponseResult中的main方法中進行操作

 public static void main(String[] args) {
        //前置
        /*
        AppHttpCodeEnum success = AppHttpCodeEnum.SUCCESS;
        System.out.println(success.getCode());
        System.out.println(success.getErrorMessage());
        */

        //查詢一個對象
        /*
        Map map = new HashMap();
        map.put("name","zhangsan");
        map.put("age",18);
        ResponseResult result = ResponseResult.okResult(map);
        System.out.println(JSON.toJSONString(result));
        */


        //新增,修改,刪除  在項目中統一返回成功即可
        /*
        ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SUCCESS);
        System.out.println(JSON.toJSONString(result));
        */


        //根據不用的業務返回不同的提示信息  比如:當前操作需要登錄、參數錯誤
        /*
        ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        System.out.println(JSON.toJSONString(result));
        */

        //查詢分頁信息
        PageResponseResult responseResult = new PageResponseResult(1, 5, 50);
        List list = new ArrayList();
        list.add("itcast");
        list.add("itheima");
        responseResult.setData(list);
        System.out.println(JSON.toJSONString(responseResult));

}

3.4.多環境切換

在每一個微服務的工程中的根目錄下創建三個文件,方便各個環境的切換

(1)maven_dev.properties

定義開發環境的配置

(2)maven_prod.properties

定義生產環境的配置

(3)maven_test.properties

定義測試環境的配置,開發階段使用這個測試環境

默認加載的環境為test,在打包的過程中maven指令也可以指定參數打包: package -P test/prod/dev

具體配置,請查看父工程下的maven插件的profiles配置

<!-- maven的profile設置,主要來實現根據maven指令參數實現多環境切換 -->
<profiles>
    <profile>
        <id>dev</id>
        <build>
            <filters>
                <filter>maven_dev.properties</filter>
            </filters>
        </build>
    </profile>
    <profile>
        <id>test</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <build>
            <filters>
                <filter>maven_test.properties</filter>
            </filters>
        </build>
    </profile>
    <profile>
        <id>prod</id>
        <build>
            <filters>
                <filter>maven_prod.properties</filter>
            </filters>
        </build>
    </profile>
</profiles>

4.平台運行端開發

4.1.頻道管理

4.1.1.需求分析

整體頁面

新增彈窗

數據庫表:頻道表:ad_channel

實體類

實體類存放位置

/**
 * <p>
 * 頻道信息表
 * </p>
 *
 * @author itheima
 */
@Data
@TableName("ad_channel")
public class AdChannel implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 頻道名稱
     */
    @TableField("name")
    private String name;

    /**
     * 頻道描述
     */
    @TableField("description")
    private String description;

    /**
     * 是否默認頻道
     */
    @TableField("is_default")
    private Boolean isDefault;

    @TableField("status")
    private Boolean status;

    /**
     * 默認排序
     */
    @TableField("ord")
    private Integer ord;

    /**
     * 創建時間
     */
    @TableField("created_time")
    private Date createdTime;

}

4.1.2.微服務搭建

1.創建springboot功能heima-leadnews-admin

2.pom.xml文件中引入依賴

<dependencies>
        <!-- 引入依賴模塊 -->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>heima-leadnews-model</artifactId>
        </dependency>
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>heima-leadnews-common</artifactId>
        </dependency>
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>heima-leadnews-apis</artifactId>
        </dependency>
    
    
        <!-- Spring boot starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>

注意:其中mybatis-plus相關的依賴在heima-leadnews-model中定義 其中實體類需要mybatis-plus的注解

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
</dependencies>

3.創建引導類和包結構

引導類

@SpringBootApplication
public class AdminApplication {

    public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class,args);
    }
}

包結構命名規范:

  • com.heima.${模塊名稱}為基礎包名 如平台管理就是 com.heima.admin
  • config 配置信息
  • controller.v1 控制層
  • feign 需要遠程調用的feign接口
  • service 業務層
  • mapper 持久層

4.application.yml文件配置

# 項目服務端口
server:
  port: 9001

spring:
  # 項目服務名稱
  application:
    name: leadnews-admin
  # 數據庫設置
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/leadnews_admin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: root
    
# 設置Mapper接口所對應的XML文件位置,如果你在Mapper接口中有自定義方法,需要進行該配置
mybatis-plus:
  mapper-locations: classpath*:mapper/*.xml
  # 設置別名包掃描路徑,通過該屬性可以給包中的類注冊別名
  type-aliases-package: com.heima.model.admin.pojos

5.在引導類中增加Mybatis-plus的mapper接口掃描操作,並設置分頁插件

@SpringBootApplication
@MapperScan("com.heima.admin.mapper")
public class AdminApplication {

    public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class,args);
    }

    /**
     * mybatis-plus分頁插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

6.新建日志文件:logback.xml(類似於log4j.properties)

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <!--定義日志文件的存儲地址,使用絕對路徑-->
    <property name="LOG_HOME" value="E:/WorkSpace/2020WorkSpace/logs"/>

    <!-- Console 輸出設置 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件輸出的文件名-->
            <fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 異步輸出 -->
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丟失日志.默認的,如果隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認的隊列的深度,該值會影響性能.默認值為256 -->
        <queueSize>512</queueSize>
        <!-- 添加附加的appender,最多只能添加一個 -->
        <appender-ref ref="FILE"/>
    </appender>


    <logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>
    <logger name="org.springframework.boot" level="debug"/>
    <root level="info">
        <!--<appender-ref ref="ASYNC"/>-->
        <appender-ref ref="FILE"/>
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

4.1.3.頻道列表-接口定義

接口詳情

1.在heima-leadnews-apis模塊中定義接口com.heima.api.admin.ChannelControllerApi

package com.heima.apis.admin;

import com.heima.model.admin.dtos.ChannelDto;
import com.heima.model.common.dtos.ResponseResult;

public interface AdChannelControllerApi {

    /**
     * 根據名稱分頁查詢頻道列表
     * @param dto
     * @return
     */
    public ResponseResult findByNameAndPage(ChannelDto dto);
}

2.在heima-leadnews-model模塊中定義實體類ChannelDto,繼承PageRequestDto

package com.heima.model.admin.dtos;

import com.heima.model.common.dtos.PageRequestDto;
import lombok.Data;

@Data
public class ChannelDto extends PageRequestDto {

    /**
     * 頻道名稱
     */
    private String name;
}

4.1.4.頻道列表-功能實現

1.在heima-leadnews-admin中的com.heima.admin.mapper包下定義持久層接口

package com.heima.admin.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.admin.pojos.AdChannel;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface AdChannelMapper extends BaseMapper<AdChannel> {
}

2.在heima-leadnews-admin中的com.heima.admin.service包下定義業務層接口

package com.heima.admin.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.admin.dtos.ChannelDto;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.common.dtos.ResponseResult;

public interface AdChannelService extends IService<AdChannel> {

    /**
     * 根據名稱分頁查詢頻道列表
     * @param dto
     * @return
     */
    public ResponseResult findByNameAndPage(ChannelDto dto);
}

在com.heima.admin.service.impl包下定義業務層接口實現類

package com.heima.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.admin.mapper.AdChannelMapper;
import com.heima.admin.service.AdChannelService;
import com.heima.model.admin.dtos.ChannelDto;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

@Service
public class AdChannelServiceImpl extends ServiceImpl<AdChannelMapper, AdChannel> implements AdChannelService {



    @Override
    public ResponseResult findByNameAndPage(ChannelDto dto) {

        //1.參數檢測
        if(dto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //分頁參數檢查
        dto.checkParam();

        //2.安裝名稱模糊分頁查詢
        Page page = new Page(dto.getPage(),dto.getSize());
        LambdaQueryWrapper<AdChannel> lambdaQueryWrapper = new LambdaQueryWrapper();
        if(StringUtils.isNotBlank(dto.getName())){
            // AdChannel::getName : 就是調用AdChannel中的getName()獲取返回結果,再在like中使用
            lambdaQueryWrapper.like(AdChannel::getName,dto.getName());
        }
        IPage result = page(page, lambdaQueryWrapper);

        //3.結果封裝
        ResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(),(int)result.getTotal());
        responseResult.setData(result.getRecords());
        return responseResult;
    }
}

3.在heima-leadnews-admin中的com.heima.admin.controller.v1包中定義ChannelController,注意:ChannelController實現接口AdChannelControllerApi

@RestController
@RequestMapping("/api/v1/channel")
public class AdChannelController  implements AdChannelControllerApi {

    @Autowired
    private AdChannelService channelService;

    @PostMapping("/list")
    @Override
    public ResponseResult findByNameAndPage(@RequestBody ChannelDto dto){
        return channelService.findByNameAndPage(dto);
    }
}

4.2.接口測試工具

啟動heima-leadnews-admin項目,進行測試。

注意事項

注意1

在install工程的時候,會出現heima-leadnews-admin工程install失敗,這個不要緊,因為heima-leadnews-admin是要啟動起來作為web項目存在的,所以不用install也可以。

注意2
啟動heima-leadnews-admin出現以下信息,不是啟動失敗

4.2.1.接口測試工具-postman

1.簡介
Postman是一款功能強大的網頁調試與發送網頁HTTP請求的Chrome插件。postman被500萬開發者和超100,000家公司用於每月訪問1.3億個API。java開發通常是作為后台開發語言,通常的項目中的接口開發需要一款測試工具來調試接口,這樣無需前端頁面也不耽誤后台代碼的開發進度,postman作為一個接口測試工具,是一個非常不錯的選擇。

官方網址:https://www.postman.com/

2.安裝

解壓資料文件夾中的軟件,安裝即可。

3.請求和響應

請求方式選擇

url輸入,並發送請求

json請求參數的設置

整體設置,並發送請求,獲取響應數據。

4.2.2.接口測試工具-swagger

1.簡介

Swagger 是一個規范和完整的框架,用於生成、描述、調用和可視化 RESTful 風格的 Web 服務(https://swagger.io/)。 它的主要作用是:

  • 使得前后端分離開發更加方便,有利於團隊協作
  • 接口的文檔在線自動生成,降低后端開發人員編寫接口文檔的負擔
  • 功能測試

Spring已經將Swagger納入自身的標准,建立了Spring-swagger項目,現在叫Springfox。通過在項目中引入Springfox ,即可非常簡單快捷的使用Swagger。

2.SpringBoot集成Swagger

2.1.引入依賴,在heima-leadnews-model模塊中引入該依賴

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
</dependency>

只需要在heima-leadnews-model中進行配置即可,因為其他微服務工程都直接或間接依賴即可。

2.2.在heima-leadnews-admin工程的config包中添加一個配置類

@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

   @Bean
   public Docket buildDocket() {
      return new Docket(DocumentationType.SWAGGER_2)
              .apiInfo(buildApiInfo())
              .select()
              // 要掃描的API(Controller)基礎包
              .apis(RequestHandlerSelectors.basePackage("com.heima"))
              .paths(PathSelectors.any())
              .build();
   }

   private ApiInfo buildApiInfo() {
      Contact contact = new Contact("黑馬程序員","","");//設置接口文檔的作者、作者主頁地址、郵箱
      //設置接口文檔的描述信息,title:標題,description:描述信息,contact:作者信息,version:版本
      return new ApiInfoBuilder()
              .title("黑馬頭條-平台管理API文檔")
              .description("平台管理服務api")
              .contact(contact)
              .version("1.0.0").build();
   }
}

2.3.Swagger常用注解

在Java類中添加Swagger的注解即可生成Swagger接口文檔,常用Swagger注解如下:

@Api:修飾整個類,描述Controller的作用 @ApiOperation:描述一個類的一個方法,或者說一個接口 @ApiParam:單個參數的描述信息

@ApiModel:用對象來接收參數

@ApiModelProperty:用對象接收參數時,描述對象的一個字段

@ApiResponse:HTTP響應其中1個描述

@ApiResponses:HTTP響應整體描述

@ApiIgnore:使用該注解忽略這個API

@ApiError :發生錯誤返回的信息

@ApiImplicitParam:一個請求參數

@ApiImplicitParams:多個請求參數的描述信息

@ApiImplicitParam屬性:
1603370669168

3.具體操作

3.1.在heima-leadnews-apis的AdChannelControllerApi中添加swagger注解

//@Api:接口集的描述    value:說明字段,tags:標識,description:描述信息
@Api(value = "頻道管理", tags = "channel", description = "頻道管理API")
public interface AdChannelControllerApi {

    /**
     * 根據名稱分頁查詢頻道列表
     * @param dto
     * @return
     */
    //@ApiOperation:接口描述
    @ApiOperation("頻道分頁列表查詢")
    public ResponseResult findByNameAndPage(ChannelDto dto);
}

3.2.在heima-leadnews-model的ChannelDto和PageRequestDto中添加swagger注解

@Data
public class ChannelDto extends PageRequestDto {

    /**
     * 頻道名稱
     */
    //@ApiModelProperty : 對象屬性描述
    @ApiModelProperty("頻道名稱")
    private String name;
}
@Data
@Slf4j
public class PageRequestDto {

    //value:描述信息,required : 是否為必須的描述
    @ApiModelProperty(value="當前頁",required = true)
    protected Integer size;
    @ApiModelProperty(value="每頁顯示條數",required = true)
    protected Integer page;

    public void checkParam() {
        if (this.page == null || this.page < 0) {
            setPage(1);
        }
        if (this.size == null || this.size < 0 || this.size > 100) {
            setSize(10);
        }
    }
}

3.3.重新啟動項目,訪問地址:http://localhost:9001/swagger-ui.html

查詢

先點擊Try it out 輸入參數,然后點擊Execute,結果如下:

4.2.3.接口測試工具-knife4j

1.簡介

knife4j是為Java MVC框架集成Swagger生成Api文檔的增強解決方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一樣小巧,輕量,並且功能強悍!

gitee地址:https://gitee.com/xiaoym/knife4j

官方文檔:https://doc.xiaominfo.com/

效果演示:http://knife4j.xiaominfo.com/doc.html

2.核心功能

該UI增強包主要包括兩大核心功能:文檔說明 和 在線調試

  • 文檔說明:根據Swagger的規范說明,詳細列出接口文檔的說明,包括接口地址、類型、請求示例、請求參數、響應示例、響應參數、響應碼等信息,使用swagger-bootstrap-ui能根據該文檔說明,對該接口的使用情況一目了然。
  • 在線調試:提供在線接口聯調的強大功能,自動解析當前接口參數,同時包含表單驗證,調用參數可返回接口響應內容、headers、Curl請求命令實例、響應時間、響應狀態碼等信息,幫助開發者在線調試,而不必通過其他測試工具測試接口是否正確,簡介、強大。
  • 個性化配置:通過個性化ui配置項,可自定義UI的相關顯示信息
  • 離線文檔:根據標准規范,生成的在線markdown離線文檔,開發者可以進行拷貝生成markdown接口文檔,通過其他第三方markdown轉換工具轉換成html或pdf,這樣也可以放棄swagger2markdown組件
  • 接口排序:自1.8.5后,ui支持了接口排序功能,例如一個注冊功能主要包含了多個步驟,可以根據swagger-bootstrap-ui提供的接口排序規則實現接口的排序,step化接口操作,方便其他開發者進行接口對接

3.快速集成

3.1.在heima-leadnews-common模塊中的pom.xml文件中引入knife4j的依賴

<!-- knife4j依賴 -->
<dependency>
     <groupId>com.github.xiaoymin</groupId>
     <artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>

3.2.在heima-leadnews-admin的SwaggerConfiguration中添加配置

@Configuration
@EnableSwagger2 //開啟Swagger支持
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfiguration {

	........

}

3.3.啟動heima-leadnews-admin,在瀏覽器輸入地址:http://localhost:9001/doc.html

查詢頻道列表

4.3.頻道管理后續功能實現

4.3.1.頻道管理-新增

后台開發流程

1.在heima-leadnews-apis的AdChannelControllerApi中增加接口

/**
 * 新增
 * @param channel
 * @return
 */
public ResponseResult save(AdChannel channel);

2.在heima-leadnews-admin的ChannelController中實現接口方法

/**
 * 新增
 * @param channel
 * @return
 */
@Override
@PostMapping("/save")
public ResponseResult save(@RequestBody AdChannel channel) {
  return channelService.insert(channel);
}

3.在heima-leadnews-admin的AdChannelService中增加接口

/**
 * 新增
 * @param channel
 * @return
 */
public ResponseResult insert(AdChannel channel);

4.在heima-leadnews-admin的AdChannelServiceImpl中實現接口方法

/**
     * 新增
     * @param adChannel
     * @return
     */
    @Override
    public ResponseResult insert(AdChannel adChannel) {
        //1.檢查參數
        if(null == adChannel){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.保存
        adChannel.setCreatedTime(new Date());
        save(adChannel);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

5.啟動項目測試

擴展

如下想要某些參數(id,createdTime)隱藏,可以在AdChannel的對應屬性上增加@ApiModelProperty(hidden = true)

4.3.2.頻道管理-修改

需求分析

有效無效狀態修改

編輯內容

1.在heima-leadnews-apis的AdChannelControllerApi中增加接口

 /**
     * 修改
     * @param adChannel
     * @return
     */
    public ResponseResult update(AdChannel adChannel);

2.在heima-leadnews-admin的ChannelController中實現接口方法

/**
     * 修改
     */
    @Override
    @PostMapping("/update")
    public ResponseResult update(@RequestBody AdChannel adChannel) {
        return channelService.update(adChannel);
    }

3.在heima-leadnews-admin的AdChannelService中增加接口

/**
     * 修改
     * @param adChannel
     * @return
     */
    public ResponseResult update(AdChannel adChannel);

4.在heima-leadnews-admin的AdChannelServiceImpl中實現接口方法

/**
     * 修改
     * @param adChannel
     * @return
     */
    @Override
    public ResponseResult update(AdChannel adChannel) {

        //1.檢查參數
        if(null == adChannel || adChannel.getId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.修改
        updateById(adChannel);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

5.啟動項目測試

4.3.3.頻道管理-刪除

需求分析

注意:如果當前狀態為有效則不能刪除

1.在heima-leadnews-apis的AdChannelControllerApi中增加接口

/**
     * 刪除
     * @param id
     * @return
     */
    public ResponseResult deleteById(Integer id);

2.在heima-leadnews-admin的ChannelController中實現接口方法

/**
     * 刪除
     * @param id
     * @return
     */
    @Override
    @GetMapping("/del/{id}")
    public ResponseResult deleteById(@PathVariable("id") Integer id) {
        return channelService.deleteById(id);
    }

3.在heima-leadnews-admin的AdChannelService中增加接口

 /**
     * 刪除
     * @param id
     * @return
     */
    public ResponseResult deleteById(Integer id);

4.在heima-leadnews-admin的AdChannelServiceImpl中實現接口方法

/**
     * 刪除
     * @param id
     * @return
     */
    @Override
    public ResponseResult deleteById(Integer id) {
        //1.檢查參數
        if(id == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.判斷當前頻道是否存在 和 是否有效
        AdChannel adChannel = getById(id);
        if(adChannel==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if(adChannel.getStatus()){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"頻道有效不能刪除");
        }

        //int i = 10/0;

        //3.刪除頻道
        removeById(id);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

5.啟動項目測試

枚舉、MavenProfile、swagger的用法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM