[java 手把手教程][第二季]java 後端部落格系統文章系統——No3

pc859107393發表於2019-03-01

又是新的一期了,這一期我們把前面文章於都相關的基本地方都完成吧。

每次新寫一章都會有很多話說,到頭來覺得這些話好像無關痛癢,畢竟我們做技術的只需要技術足夠,除非不只做技術才會想更多。

說實話,最近經歷了很多事情,只想說:時間才是最長的告白。

專案github地址:github.com/pc859107393…

我的簡書首頁是:www.jianshu.com/users/86b79…

上一期是:[手把手教程][第二季]java 後端部落格系統文章系統——No2

[java 手把手教程][第二季]java 後端部落格系統文章系統——No3
行走的java全棧

工具

  • IDE為idea16
  • JDK環境為1.8
  • gradle構建,版本:2.14.1
  • Mysql版本為5.5.27
  • Tomcat版本為7.0.52
  • 流程圖繪製(xmind)
  • 建模分析軟體PowerDesigner16.5
  • 資料庫工具MySQLWorkBench,版本:6.3.7build

本期目標

  • 文章閱讀前端頁面全部完成
  • 根據頁面框架進行解耦
    • 頁面附屬資訊
    • 文章資訊

文章系統前端頁面

文章系統作為我們部落格系統中重要的一環,我們需要的不僅僅是文章系統,更多的是可以理解成一個自媒體平臺,我們的核心價值通過這個體現出來了,才能把其他的東西做好。

上次我們的文章中可以看到前端頁面的一些東西,主要是:

  • 文章列表(文章內容)
  • 文章分類導航
  • 標籤聚合導航
  • 站點基本資訊
  • 等···

具體顯示資訊如圖所示:

[java 手把手教程][第二季]java 後端部落格系統文章系統——No3
我的部落格第四章文章系統-首頁結構

從上面的截圖中我們可以看到我們的頁面大概結構,頁面頭、頁面尾和頁面中間的內容,那麼出於便利考慮我們需要把頭尾單獨抽取出來存放,頁面其他的內容我們需要根據需要處理。現在我們先不考慮那麼多,我們只是基於程式合理建設的角度來說,我們需要把頁面上面動態變化的資訊都獨立做成介面來供外部呼叫,然後一般不怎麼變化的東西我們就直接固化到頁面中,即是說:

  • 中間的列表我們採用分頁載入,全部動態從介面獲取
  • 上面的一些其他變化的資訊我們從請求的時候就附加給它

所以,我們需要把前面的首頁介面重寫一下。

首先,我們給首頁獲取資料的介面打上過時的標記。

/**
     * 獲取主頁的json資料,按照道理講這裡應該根據頁面結構拆分組合的
     *
     * @param user 使用者資訊
     * @return 返回首頁json
     */
    @RequestMapping(value = "/home"
            , produces = "application/json; charset=utf-8")
    @ResponseBody
    @Deprecated
    public Object getHomeJson(User user) {
        //此處程式碼省略
    }複製程式碼

既然我們已經把首頁的設定為過時,那麼新的介面必須對照著做一個,那麼我們需要怎麼處理呢?按照前面的思路來講,我們現在需要根據需求將我們頁面資訊拆分成多個介面,首先需要把左邊我們圈出來的部分整合到一起,那麼我們需要先把個人資訊分類導航和標籤聚合這幾個獨立出來,所以得我們直接上程式碼。

    /**
     * 返回主頁面
     *
     * @return
     */
    @RequestMapping("/main")
    public ModelAndView frontMain(HttpServletRequest request) throws Exception {
        ModelAndView view = new ModelAndView("frontMain");
        //把資料存入前端,避免前端再次發起網路請求
        view.addObject("framJson", getFramJson());
        view.addObject("postsJson", findPublishPost(request, 1, 10));
        return view;
    }

    /**
     * 頁面框架的變化資訊
     * 1、個人資訊
     * 2、最新熱點隨機文章資訊
     * 3、標籤資訊
     *
     * @return
     */
    @RequestMapping(value = "/getFramJson"
            , produces = "application/json; charset=utf-8")
    @ResponseBody
    public Object getFramJson() {
        HomeBean homeBean = new HomeBean(); //首頁內容
        HomeBean.DataBean dataBean = new HomeBean.DataBean();   //首頁下面的Data內容物件
        try {
            int toalNum = postService.getAllCount();    //先把總條數賦值給總頁數,作為快取變數,減少下面演算法的查詢次數
            toalNum = toalNum % 10 > 0 ? toalNum / 10 + 1 : toalNum / 10;     //在每頁固定條數下能不能分頁完成,有餘則加一頁碼

            List<PostBean> newData = postService.findAllNew();
            if (null == newData || newData.isEmpty()) {
                //頁面上面推薦的文章資訊不為空
                dataBean.setNewPosts(null);
                dataBean.setHotPosts(null);
                dataBean.setRandomPosts(null);
            } else {
                //首頁文章列表資訊設定
                dataBean.setNewPosts(newData);
                dataBean.setHotPosts(newData);
                dataBean.setRandomPosts(newData);
            }
            //日期歸檔
            List<DateCountBean> allPostDateCount = postService.getAllPostDateCount();
            if (null != allPostDateCount && !allPostDateCount.isEmpty()) {
                dataBean.setDate(allPostDateCount);
            } else {
                dataBean.setDate(null);
            }
            //設定作者資訊
            List<HashMap<String, String>> userMeta = userService.getUserMeta(1);
            dataBean.setAuthor(userMeta);

            homeBean.setData(dataBean);
            homeBean.setCode(ResponseObj.OK);
            homeBean.setMsg(ResponseList.OK_STR);
            return new GsonUtils().toJson(homeBean);
        } catch (Exception e) {
            e.printStackTrace();
            //查詢失敗
            homeBean.setCode(ResponseObj.FAILED);
            homeBean.setMsg(ResponseList.FAILED_STR);
            return new GsonUtils().toJson(homeBean);
        }
    }複製程式碼

說實話感覺上面沒啥解釋的,說白了就是將資料按照一定的結構組合起來,具體展示的json如何,我們可以直接在下面看:

{
  "msg" : "success",
  "data" : {
    "author" : [
      {
        "meta_key" : "nickname",
        "meta_value" : "雨下一整夜"
      },
      {
        "meta_key" : "description",
        "meta_value" : "我想在最好的時候遇見最美的你。那是最美。"
      },
      {
        "meta_key" : "managenav-menuscolumnshidden",
        "meta_value" : "a:2:{i:0;s:11:\"css-classes\";i:1;s:11:\"description\";}"
      }
    ],
    "pageNum" : 0,
    "pageSize" : 0,
    "newPosts" : [
      {
        "id" : "286",
        "postTitle" : "Android-MVP架構"
      },
      {
        "id" : "282",
        "postTitle" : "[手把手教程][JavaWeb]優雅的SpringMvc+Mybatis應用(八)"
      }
    ],
    "date" : [
      {
        "date" : "2015-11-13",
        "idList" : [
          "71",
          "69",
          "67"
        ]
      },
      {
        "date" : "2015-10-15",
        "idList" : [
          "48",
          "46"
        ]
      }
    ],
    "randomPosts" : [
      {
        "id" : "286",
        "postTitle" : "Android-MVP架構"
      },
      {
        "id" : "282",
        "postTitle" : "[手把手教程][JavaWeb]優雅的SpringMvc+Mybatis應用(八)"
      }
    ],
    "hotPosts" : [
      {
        "id" : "286",
        "postTitle" : "Android-MVP架構"
      },
      {
        "id" : "282",
        "postTitle" : "[手把手教程][JavaWeb]優雅的SpringMvc+Mybatis應用(八)"
      }
    ],
    "totalNum" : 0
  },
  "code" : 1
}複製程式碼

通過上面我們組織的json,我們可以很清晰明瞭的看到我們的資料結構是根據頁面結構來組合的,所以我們需要資料的時候對應著取值就可以解決問題。

說了這麼多後端的介面,我們現在需要拿資料去前臺展示,所以我們需要在前端獲取資料。前臺資料展示還是使用doT.min.js來展示,程式碼如下:


    var framJsonStr = JSON.stringify(${framJson});
    var framJsonObj = JSON.parse(framJsonStr);
    var postsJsonStr = JSON.stringify(${postsJson});
    var postsJsonObj = JSON.parse(postsJsonStr);
    var pageNum = postsJsonObj.pageNum;
    var pageSize = postsJsonObj.pageSize;
    var totalNum = postsJsonObj.totalNum;
    var authorDes = "<p class=\"text-center\">" + framJsonObj.data.author[0].meta_value + "<br/>" + framJsonObj.data.author[1].meta_value + "</p>";
    document.getElementById("authorDescription").innerHTML = authorDes;

    if (framJsonObj.code == 1) {
        pagefn = doT.template($("#listHot").html());   //初始化列表模板
        updateHotList(framJsonObj.data.hotPosts);   //更新資料
        updateNewList(framJsonObj.data.newPosts);   //更新資料
        updateRandList(framJsonObj.data.randomPosts);   //更新資料
    }

    function updateHotList(data) {
        $("#listHot").empty(); //清空模板資料
        $("#hotList").html(pagefn(data));   //加入資料到模板
    }

    function updateNewList(data) {
        $("#listNew").empty(); //清空模板資料
        $("#newList").html(pagefn(data));   //加入資料到模板
    }

    function updateRandList(data) {
        $("#listRand").empty(); //清空模板資料
        $("#randList").html(pagefn(data));   //加入資料到模板
    }

    //開始載入列表資料
    if (postsJsonObj.code == 1) {
        pagefn = doT.template($("#pagetmpl").html());   //初始化列表模板
        updateList(postsJsonObj.data);   //更新資料
    } else {
        alert("獲取資料失敗!請聯絡管理員");
    }

    function updateList(data) {
        $("#pagetmpl").empty(); //清空模板資料
        $("#blog-table-list").html(pagefn(data));   //加入資料到模板
    }
    function goToNextPage() {
        pageNum = parseInt(pageNum) + 1;
        $.ajax({
            type: "POST",
            url: '<c:url value="/front/findPublishPost"/>',
            data: {pageNum: pageNum, pageSize: pageSize},
            dataType: 'json', //當這裡指定為json的時候,獲取到了資料後會自己解析的,只需要 返回值.欄位名稱 就能使用了
            cache: false,
            success: function (data) {
                if (data.code == 1) {
                    updateList(data.data);
                    pageNum = data.pageNum;
                    $("#log-controller-now").html(pageNum);
                }
            }
        });
    }

    function goToLastPage() {
        pageNum = parseInt(pageNum) - 1;
        $.ajax({
            type: "POST",
            url: '<c:url value="/front/findPublishPost"/>',
            data: {pageNum: pageNum, pageSize: pageSize},
            dataType: 'json', //當這裡指定為json的時候,獲取到了資料後會自己解析的,只需要 返回值.欄位名稱 就能使用了
            cache: false,
            success: function (data) {
                if (data.code == 1) {
                    updateList(data.data);
                    pageNum = data.pageNum;
                    $("#log-controller-now").html(pageNum);
                }
            }
        });
    }複製程式碼

上面我們可以看到pagefn用了幾次,這個是doT的關鍵詞,意思是設定模板。

<!--顯示文章列表-->
<div id="blog-table-list">
    <script id="pagetmpl" type="text/x-dot-template">
        {{ for(var i=0; i
        < it.length ; i++){ }} <a href="<c:url value=" /front/post/{{=it[i].id}} "/>">
            <div style="width:100%;height: 2px"></div>
            <%--<div class="kratos-entry-thumb clearfix">--%>
            <%--<img class="kratos-entry-thumb"--%>
            <%--src="<c:url value="/static/images/kratos-update.png"/>">--%>
            <%--</div>--%>
            <div class="kratos-post-inner">
                <header class="kratos-entry-header clearfix">
                    <h1 class="kratos-entry-title">{{=it[i].postTitle}}</h1>
                </header>
                <div class="kratos-entry-content clearfix">
                    <p>{{=it[i].postTitle}}</p>
                </div>
            </div>
            <div style="background: #CCCCCC;width:100%;height: 2px"></div>
            </a>
            {{ } }}
    </script>
</div>

<!-- 展示文章導航 -->
<div class="tab-content">
    <div class="tab-pane fade" id="newest">
        <ul class="list-group" id="newList">
            <script id="listNew" type="text/x-dot-template">
                {{ for(var i=0; i
                < it.length ; i++){ }} 
                <a class="list-group-item visible-lg"
                    title="{{=it[i].postTitle}}" 
                    href="<c:url value=" /front/post/{{=it[i].id}} "/>" 
                    rel="bookmark">
                    <i class="fa fa-book"> {{=it[i].postTitle}}</i> 
                </a>
                    {{ } }}</script>
        </ul>
    </div>
    <div class="tab-pane fade  in active" id="hot">
        <ul class="list-group" id="hotList">
            <script id="listHot" type="text/x-dot-template">
                {{ for(var i=0; i
                < it.length ; i++){ }} 
                <a class="list-group-item visible-lg"
                    title="{{=it[i].postTitle}}" 
                    href="<c:url value=" /front/post/{{=it[i].id}} "/>" 
                    rel="bookmark">
                <i class="fa fa-book"> {{=it[i].postTitle}}</i> 
                </a>
                    {{ } }}</script>
        </ul>
    </div>
    <div class="tab-pane fade" id="rand">
        <ul class="list-group" id="randList">
            <script id="listRand" type="text/x-dot-template">
                {{ for(var i=0; i
                < it.length ; i++){ }} 
                <a class="list-group-item visible-lg"
                    title="{{=it[i].postTitle}}" 
                    href="<c:url value=" /front/post/{{=it[i].id}} "/>" 
                    rel="bookmark">
                    <i class="fa fa-book"> {{=it[i].postTitle}}</i> 
                </a>
                    {{ } }}</script>
        </ul>
    </div>
</div>Ï複製程式碼

其實在上面的程式碼中我們可以看到doT模板和其他的都差不多,無非就是按照固定的格式組裝資料,反正就是網頁怎麼寫的,然後把格式套上,然後按照格式輸出就行了。

做到這裡後,我們就能看到做出的結果是什麼樣子的了。效果圖暫時不上,大家後面自行下載專案執行就知道了。

既然這裡做了,那麼我們勢必要做一下專案的文章詳情。文章詳情我們應該怎麼辦呢? 我們需要通過關鍵資料去查詢到具體的文章資訊

我們可以看到上面的json資料中包含一個id的欄位,然後我們對照資料庫會看到id和資料庫的id也是對應的。所以我們需要用介面實現通過ID查詢資料庫對應的文章資訊。思路有了,那麼程式碼實現就是很容易的,直接程式碼如下:

    /**
     * RESTful風格的文章頁面
     * @RequestMapping(path = "/post/{postId}", method = RequestMethod.GET)
     * 通過上面的語句配置訪問路徑/post/後面指定是文件ID,在下面的方法引數中配置註解@PathVariable可以自動賦值,然後獲取資料。
     * @param postId 文章ID
     * @return 返回文章頁面
     */
    @RequestMapping(path = "/post/{postId}", method = RequestMethod.GET)
    public ModelAndView getPostView(@PathVariable int postId) {
        ModelAndView resultView = new ModelAndView("frontPost");
        resultView.addObject("framJson", getFramJson());
        resultView.addObject("postJson", getPostById(postId));
        return resultView;
    }


    /**
     * 根據文章ID獲取文章內容
     *
     * @param postId 文章ID
     * @return 返回文章ID對應的文章內容
     */
    @RequestMapping(value = "/getPost"
            , produces = "application/json; charset=utf-8")
    @ResponseBody
    public Object getPostById(int postId) {
        ResponseObj<Object> responseObj = new ResponseObj<>();
        try {
            PostBean postBean = postService.findPostById(postId);
            if (null == postBean) {
                responseObj.setCode(ResponseObj.EMPUTY);
                responseObj.setMsg(ResponseObj.EMPUTY_STR);
            } else {
                responseObj.setCode(ResponseObj.OK);
                responseObj.setMsg(ResponseObj.OK_STR);
                responseObj.setData(postBean);
            }
            return new GsonUtils().toJson(responseObj);
        } catch (Exception e) {
            e.printStackTrace();
            responseObj.setCode(ResponseObj.FAILED);
            responseObj.setMsg(ResponseObj.FAILED_STR);
            return new GsonUtils().toJson(responseObj);
        }
    }複製程式碼

上面的程式碼一個是RESTful風格請求地址的文章頁面,一個是api介面訪問地址。下面分別是Service和Dao層的程式碼。

    /**
    *這是Service
    */
    @Override
    public PostBean findPostById(Serializable postId) {
        return dao.findOneById(postId);
    }

    /**
    *這是Dao
    */
    @Override
    PostBean findOneById(Serializable postId);

    <!-- 這是mapper -->
    <select id="findOneById" resultType="cn.acheng1314.domain.PostBean">
        SELECT
          `ID`,`post_title`,`post_date`,`post_content`
        FROM
          `wp_posts`
        WHERE
        `ID`=#{postId}
        AND
        `post_status`='publish'

    </select>複製程式碼

最後頁面的樣子如下,頁面展示程式碼也就是獲取到內容直接輸出,具體程式碼就不貼了,影響文章篇幅。大家可以直接到我的GitHub上面下載。

[java 手把手教程][第二季]java 後端部落格系統文章系統——No3
我的部落格第四章文章系統-文章頁面

到目前為止,我們前端的頁面基本完成後面是需要把頁面頭和頁面尾獨立出來,然後我們這種部分方便定製。

那麼目前前端頁面相關的東西基本完成,這一期也結束了,後面我們就是配置系統後端了。加油,有興趣額的一起來吧。

相關文章