[手把手教程][第二季]java 後端博客系統文章系統——No2

這是博客系統的第三章,也是堅持寫這個系列文章的第三個月了。在這期間我創建了全棧技術交流羣,感謝一路鼓勵個人朋友們。也要感謝個人大學導師,是他們在我須要的時候,告訴我作人的品質。javascript

今天這一篇,主要是關於上一張的編碼實現。爲何我要單路分離出來?由於作事要分前後,明白道理,執行才能肯定無誤。html

咱們仍是從老套路(Dao→Service→Controller)作起來,讓咱們先看看數據存儲相關的東西吧。前端

項目github地址:github.com/pc859107393…java

個人掘金首頁是:gold.xitu.io/user/57899e…jquery

上一期是:[手把手教程][第二季]java 後端博客系統文章系統——No1git


wordpress作的文章存儲

在上次咱們已經看過了wordpress的數據庫模型(有朋友問我什麼是逆向分析,拿着別人的產品逆向推導這就是逆向分析),咱們能夠很清楚的看到數據庫關於文章存儲的兩張表,它們分別存儲了文章的主體信息和文章的其餘信息,具體的咱們再看看數據庫模型:github

第二章文章系統-wordpress數據庫模型

在上面的途中,咱們很明顯的看到數據庫關於文章的存儲主要分爲兩張表:ajax

  • wp_posts 存放文章主體信息
  • wp_postmeta 存放文章的附加信息

咱們先看看wp_posts的主要結構:算法

DROP TABLE IF EXISTS `wp_posts`;
CREATE TABLE `wp_posts` (
  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `post_author` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '做者ID',
  `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '文章建立時間',
  `post_date_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '文章最近修改時間',
  `post_content` longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文章內容',
  `post_title` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文章標題',
  `post_excerpt` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `post_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'publish' COMMENT '文章狀態',
  `comment_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'open' COMMENT '評論狀態',
  `ping_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'open' COMMENT 'ping狀態',
  `post_password` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '文章密碼',
  `post_name` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '文章名字',
  `to_ping` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `pinged` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `post_modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_modified_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_content_filtered` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
  `post_parent` bigint(20) unsigned NOT NULL DEFAULT '0',
  `guid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  `menu_order` int(11) NOT NULL DEFAULT '0',
  `post_type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'post' COMMENT '文章類型',
  `post_mime_type` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '文件類型',
  `comment_count` bigint(20) NOT NULL DEFAULT '0' COMMENT '評論數',
  PRIMARY KEY (`ID`),
  KEY `type_status_date` (`post_type`,`post_status`,`post_date`,`ID`),
  KEY `post_parent` (`post_parent`),
  KEY `post_author` (`post_author`),
  KEY `post_name` (`post_name`(191))
) ENGINE=InnoDB AUTO_INCREMENT=289 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;複製代碼

上面的數據庫表,也是根據表生成的sql語句,固然註釋是我加上去的。spring

可能看到這裏不少朋友說咱們如今只看到了表結構,並且又是你添加的註釋,你這想怎麼忽悠就怎麼忽悠。既然這樣,咱們不妨一看數據庫。

個人博客第三章文章系統-數據庫截圖片斷

嗯,上面的圖須要放大後纔看得清楚== 這個有點尷尬。
從上面的圖中咱們能夠看到以下關鍵信息:

id post_author post_date post_content post_title post_status post_type post_mime_type
286 1 2016-11-22 18:51:37 這是文章內容 Android-MVP架構 publish post
277 1 2016-11-08 00:37:07 ssm應用七-訪問列表-流程圖 inherit attachment image/png
23 1 2015-09-26 22:55:30 YKT主要界面示例--源碼 inherit attachment application/rar

關鍵信息就和上面的相似了,爲何我挑選這三個:

  • 博客的系統,主要的就是文章存儲和多媒體資源存儲
  • 上面三個分別表明了文章、圖片、rar壓縮包
  • 上面這個縮略表,恰好提取了目前咱們可能會用到區分的不一樣信息

沒時間解釋了,咱們直接分析上面的數據庫表中的字段。

  • 文章
    • post_content通常有內容
    • post_status 會有多種狀態
    • post_type 指向文章 post
    • post_mime_type爲空
  • 多媒體文件
    • post_content 爲空
    • post_status通常是inherit
    • post_type 通常是 attachment
    • post_mime_type 通常爲具體的文件類型,如: image/png、 application/rar

因此根據上面的一些信息,咱們能夠開始實現咱們文章系統下面的文章列表接口了,首先按照老規矩實現Dao層:

import cn.acheng1314.domain.PostBean;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.util.List;

/** * Created by 程 on 2016/11/27. */
@Repository
public interface PostDao extends Dao<PostBean> {

    @Override
    int add(PostBean postBean);

    @Override
    int del(PostBean postBean);

    @Override
    int update(PostBean postBean);

    @Override
    PostBean findOneById(Serializable Id);

    @Override
    List<PostBean> findAll();


    List<PostBean> findAllNew();

    /** * 分頁查詢 * * @param offset 起始位置 * @param limit 每頁數量 * @return */
    List<PostBean> findAllPublish(@Param("offset") int offset, @Param("limit") int limit);

    /** * 獲取總的條數 */
    int getAllCount();

    List<PostBean> getAllPostDateCount();
}複製代碼

其實上面的接口咱們能夠看到和之前的差很少,畢竟數據庫操做就是一些基本的增刪改查。沒道理的,咱們必須接着實現mapper,mapper以下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.acheng1314.dao.PostDao"> <select id="findAllPublish" resultType="cn.acheng1314.domain.PostBean"> SELECT `ID`,`post_title`,`post_date`,`post_content` FROM `wp_posts` WHERE `post_type`='post' AND `post_status`='publish' ORDER BY `ID` DESC LIMIT #{offset}, #{limit} </select> <select id="findAllNew" resultType="cn.acheng1314.domain.PostBean"> SELECT `ID`,`post_title` FROM `wp_posts` WHERE `post_type`='post' AND `post_status`='publish' ORDER BY `ID` DESC LIMIT 1, 10 </select> <select id="getAllCount" resultType="int"> SELECT COUNT(*) FROM `wp_posts`; </select> <select id="getAllPostDateCount" resultType="cn.acheng1314.domain.PostBean"> SELECT `post_date`,`ID` FROM `wp_posts` WHERE `post_type`='post' AND `post_status`='publish' ORDER BY `post_date` DESC </select> </mapper>複製代碼

咱們上面惟一須要注意的就是咱們的文章查詢的時候,必須指定post_type='post'和post_status='publish',這樣咱們首頁展現的文章列表就是公開的文章。

每次按照套路來,你們都會知道我這邊Dao層完成後,就應該進行Service層的操做,那麼咱們看下這裏咱們的Service層是怎麼回事。

import cn.acheng1314.domain.DateCountBean;
import cn.acheng1314.domain.PostBean;
import cn.acheng1314.service.BaseService;

import java.util.List;

/** * Created by 程 on 2016/11/27. */
public interface PostService extends BaseService<PostBean> {
    @Override
    void add(PostBean postBean) throws Exception;

    @Override
    List<PostBean> findAll(int pageNum, int pageSize);

    List<PostBean> findAllPublish(int pageNum, int pageSize);

    /** * 獲取總條數 * @return 獲取總條數 */
    int getAllCount();

    /** * 獲取熱點文章 * @return */
    List<PostBean> findAllNew();

    /** * 獲取全部文章的日期歸檔 * @return 返回歸檔信息 */
    List<DateCountBean> getAllPostDateCount();
}


import cn.acheng1314.dao.PostDao;
import cn.acheng1314.domain.DateCountBean;
import cn.acheng1314.domain.PostBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/** * Created by 程 on 2016/11/27. */
@Service("postService")
public class PostServiceImpl implements PostService {

    @Autowired
    private PostDao dao;

    @Override
    public void add(PostBean postBean) throws Exception {

    }

    @Override
    public List<PostBean> findAll(int pageNum, int pageSize) {
        return null;
    }

    @Override
    public List<PostBean> findAllPublish(int pageNum, int pageSize) {
        //由於數據庫內容是從第一條出的數據,因此咱們查詢的 起始位置 = 頁碼 * 條數 + 1;
        pageNum -= 1;
        return dao.findAllPublish(pageNum * pageSize + 1, pageSize);
    }

    @Override
    public int getAllCount() {
        return dao.getAllCount();
    }

    @Override
    public List<PostBean> findAllNew() {
        return dao.findAllNew();
    }

    @Override
    public List<DateCountBean> getAllPostDateCount() {
        /* * 這裏存入的json數據爲: * [ {"date": "2015-11-16", "idList": [ "75", "73"] } ] * 解釋一下:日期歸檔自己應該是個下拉列表。下拉列表中的某個item包含了這個日期有:文章數量,文章ID */
        List<PostBean> tmpList = new ArrayList<>(); //建立日期歸檔的數據集合
        if (null != dao.getAllPostDateCount()
                && !dao.getAllPostDateCount().isEmpty()) {  //從dao層獲取的文章日期和ID的集合不爲空
            tmpList.addAll(dao.getAllPostDateCount());  //先將獲取的數據存入緩存變量中,避免屢次讀取數據庫
            List<DateCountBean> myDateCount = new ArrayList<>();    //建立一個日期歸檔的集合格式和上面所訴的json數據格式相同
            //也就是外層是一個集合,裏面是多個對象
            for (PostBean tmpBean : tmpList) {  //遍歷獲取文章信息數據
                DateCountBean dateCountBean = new DateCountBean();  //建立文章信息緩存的對象
                if (!myDateCount.isEmpty() &&
                        DateFormat.getDateInstance().format(tmpBean.getPostDate().getTime()).equals(myDateCount.get(myDateCount.size() - 1).getDate())) {
                    //上一個日期和當前日期的相同,則只需存入ID
                    myDateCount.get(myDateCount.size() - 1).getIdList().add(tmpBean.getId());
                } else {    //集合爲或者上一條的日期和當前的日期不相同,添加一條數據
                    //把文章緩存信息添加到集合中
                    dateCountBean.setDate(DateFormat.getDateInstance().format(tmpBean.getPostDate().getTime()));    //日期格式化,把date格式化爲String,也就是2015-09-28 00:01:07 ==> 2015-09-28
                    List<String> idList = new ArrayList<>();
                    idList.add(tmpBean.getId());
                    dateCountBean.setIdList(idList);
                    myDateCount.add(dateCountBean);
                }
            }
            return myDateCount;
        } else return null;
    }
}複製代碼

在上面我這裏在一個Service層的一個接口寫這麼多代碼?對的,沒錯,咱們強調的是Service層用來作數據驅動,那麼咱們須要在Service層完成一些基本數據的組織。因此說咱們最後首頁的數據以下:

{
    "code": 1,
    "msg": "success",
    "data": {
        "date": [
            {
                "date": "2016-5-19",
                "idList": [
                    "192",
                    "191"
                ]
            },
            {
                "date": "2016-3-30",
                "idList": [
                    "187"
                ]
            }
        ],
        "posts": [
            {
                "id": "282",
                "postDate": "Nov 16, 2016 12:51:13 AM",
                "postContent": "多角色控制思路整理</h3>\r\n關於多角色控制,起始用戶角色按照用戶職能分工,通常來講思路以下",
                "postTitle": "[手把手教程][JavaWeb]優雅的SpringMvc+Mybatis應用(八)"
            },
            {
                "id": "278",
                "postDate": "Nov 9, 2016 8:46:18 PM",
                "postContent": "其實分頁列表也沒什麼,重點在於作出<strong>列表局部刷新,減小頁面請求</strong>。\r\n\r\n咱們先要新建一個頁面用來顯示列表,因爲咱們的後臺網頁結構基本已經固定,因此咱們在後臺主頁那邊設定一個訪問入口,而後連接上咱們的網頁。這裏我把左邊的一個菜單改爲了列表,具體效果如圖:",
                "postTitle": "[手把手教程][JavaWeb]優雅的SpringMvc+Mybatis應用(七)"
            }
        ],
        "newPosts": [
            {
                "id": "282",
                "postTitle": "[手把手教程][JavaWeb]優雅的SpringMvc+Mybatis應用(八)"
            },
            {
                "id": "278",
                "postTitle": "[手把手教程][JavaWeb]優雅的SpringMvc+Mybatis應用(七)"
            },
            {
                "id": "192",
                "postTitle": "正如雨下"
            }
        ],
        "totalNum": 19,
        "pageNum": 1,
        "pageSize": 10
    }
}複製代碼

因爲後臺數據存儲的是富文本,因此咱們能看到有不少網頁標籤。

光是這樣說明也是沒有多少實際意義的,咱們仍然須要曬一曬具體的Controller的方法的代碼,以下:

@RequestMapping("/main")
    public ModelAndView frontMain() {
        ModelAndView view = new ModelAndView("frontMain");
        view.addObject("homeJson", getHomeJson(null));  //把首頁須要的json數據直接扔到view裏面,在下面的js代碼中能夠看到如何使用
        return view;
    }

    @RequestMapping(value = "/home"
            , produces = "application/json; charset=utf-8")
    @ResponseBody
    public Object getHomeJson(User user) {
        if (null != user) {
            //埋點,AOP日誌記錄
        }
        HomeBean homeBean = new HomeBean(); //首頁內容
        HomeBean.DataBean dataBean = new HomeBean.DataBean();   //首頁下面的Data內容對象
        try {
            int toalNum; //總頁碼

            toalNum = postService.getAllCount();    //先把總條數賦值給總頁數,做爲緩存變量,減小下面算法的查找次數

            toalNum = toalNum % 10 > 0 ? toalNum / 10 + 1 : toalNum / 10;     //在每頁固定條數下能不能分頁完成,有餘則加一頁碼

            List<PostBean> postsData = postService.findAllPublish(1, 10);   //首頁下面的文章內容
            List<PostBean> newData = postService.findAllNew();   //首頁下面的文章內容
            if (null == postsData || postsData.isEmpty()) {
                dataBean.setPosts(null);
            } else {
                dataBean.setPosts(postsData);   //首頁文章列表信息設定
            }
            if (null == newData || newData.isEmpty()) {
                dataBean.setNewPosts(null);
            } else {
                dataBean.setNewPosts(newData);   //首頁文章列表信息設定
            }
            List<DateCountBean> allPostDateCount = postService.getAllPostDateCount();
            if (null != allPostDateCount && !allPostDateCount.isEmpty()) {
                dataBean.setDate(allPostDateCount);
            } else {
                dataBean.setDate(null);
            }
            dataBean.setPageNum(1);
            dataBean.setPageSize(10);
            dataBean.setTotalNum(toalNum);
            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);
        }
    }複製代碼

注意看我上面代碼的Try-Catch處理,我這裏基本目標是保證程序功能正常,不會由於我這邊的異常而產生其餘錯誤信息。

那咱們都看到了首頁上面的一些數據,那麼咱們如今是否是須要查看前端頁面的完成呢?此處沒必要驚慌,前端頁面的完成,咱們是不會少的,並且這一期完成後的代碼,我也同樣會同步到github。

如今咱們須要的是把前臺頁面列表加載出來而且實現局部刷新。so,咱們須要先獲取到前臺頁面,具體代碼省略,咱們展現核心代碼:

<!--從modelAndView的名爲「homeJson」的Object中獲取數據,而且轉換位json的字符串,而後再轉換位json對象-->
    var homeJsonStr = JSON.stringify(${homeJson});
    var homeJsonObj = JSON.parse(homeJsonStr);
    var pageNum = homeJsonObj.data.pageNum; //獲取當前頁碼
    var pageSize = homeJsonObj.data.pageSize;   //獲取頁面長度
    var totalNum = homeJsonObj.data.totalNum;   //獲取總得頁碼
    if (homeJsonObj.code == 1) {    //獲取數據成功
        pagefn = doT.template($("#pagetmpl").html());   //初始化列表模板
        updateList(homeJsonObj.data.posts);   //更新數據
    } 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);
                }
            }
        });
    }複製代碼

固然上面的代碼,必須有jquery、doT.min.js和json2.js才能運行。

最後出來的總體效果,也就和上一章的截圖相似,具體圖片就再也不截圖了。稍後上傳本章的代碼到github

相關文章
相關標籤/搜索