【SpringBoot搭建个人博客】- 博客首页显示(十)

1,778 阅读8分钟

博客地址:ONESTARの客栈

源码领取方式一:

  • 扫一扫文末二维码,关注公众号【编程日刊】,后台回复【博客】,即可领取源码

源码领取方式二:

欢迎给star以鼓励(^_−)☆


本文将根据首页显示来进行讲述,首页有搜索、最新博客推荐显示、文章列表显示、博客信息统计等功能,将从这几个方面进行讲述,主要是后端的开发,前端只会给出部分代码,不会进行讲解。

一、首页显示

分析

问:首页显示需要考虑到哪些问题?

答:根据前端页面功能来看,两个最基本的查询,一个是查询最新文章列表,一个是查询最新推荐文章;还有就是搜搜索功能,根据关键字搜索博客;另外,就是统计博客信息,有博客总数、访问总数、评论总数、留言总数。

问:那各个功能需要怎样设计呢?

答: 这里根据每个功能来进行分析

  • 查询最新文章列表:定义一个FirstPageBlog首页实体类来查询首页文章列表信息,并定义getAllFirstPageBlog接口来关联SQL实现查询功能
  • 查询最新推荐文章:定义一个RecommendBlog实体类来查询推荐文章列表信息,并定义getRecommendedBlog接口来关联SQL实现查询
  • 搜索博客:由于搜索博客显示的还是博客列表信息,所以还是通过FirstPageBlog实体类来显示查询信息,并定义getSearchBlog接口来关联SQL实现查询功能
  • 统计博客信息:统计博客信息分别定义了getBlogTotal、getBlogViewTotal、getBlogCommentTotal、getBlogMessageTotal接口来关联SQL实现博客总数、访问总数、评论总数、留言总数的统计

1. 定义实体类

根据上面的分析,这里需要定义两个查询实体类,分别是:最新博客列表实体类(FirstPageBlog)、最新推荐实体类(RecommendBlog)

最新博客列表实体类

由于首页最新博客列表除了需要显示博客信息外,还需要显示分类、作者等信息,所以还需要定义分类名称和用户名、用户头像属性。在queryvo包下创建FirstPageBlog实体类,代码如下(省去get、set、toString方法):

package com.star.queryvo;

import java.util.Date;

/**
 * @Description: 首页博客信息实体类
 * @Date: Created in 9:39 2020/6/19
 * @Author: ONESTAR
 * @QQ群: 530311074
 * @URL: https://onestar.newstar.net.cn/
 */
public class FirstPageBlog {
    
    //博客信息
    private Long id;
    private String title;
    private String firstPicture;
    private Integer views;
    private Integer commentCount;
    private Date updateTime;
    private String description;

    //分类名称
    private String typeName;

    //用户名
    private String nickname;
    //用户头像
    private String avatar;

}
最新推荐实体类

最新推荐只要显示博客标题、首图信息,但要注意这里要体现出是否推荐到推荐栏来,所以还要有个boolean类型的变量recommend,如下(省去get、set、toString方法):

package com.star.queryvo;

/**
 * @Description: 推荐博客数据实体类
 * @Date: Created in 9:47 2020/6/19
 * @Author: ONESTAR
 * @QQ群: 530311074
 * @URL: https://onestar.newstar.net.cn/
 */
public class RecommendBlog {

    private Long id;
    private String title;
    private String firstPicture;
    private boolean recommend;

}

2. 持久层接口

由于都是和博客相关的接口,这里就都写在BlogDao类中,mapper和业务层也是一样。根据上面的分析,这里需要定义以下接口,在BlogDao接口中添加如下:

//查询首页最新博客列表信息
List<FirstPageBlog> getFirstPageBlog();

//查询首页最新推荐信息
List<RecommendBlog> getAllRecommendBlog();

//搜索博客列表
List<FirstPageBlog> getSearchBlog(String query);

//统计博客总数
Integer getBlogTotal();

//统计访问总数
Integer getBlogViewTotal();

//统计评论总数
Integer getBlogCommentTotal();

//统计留言总数
Integer getBlogMessageTotal();

3. mapper

mapper是和持久层接口相对应的,在BlogDao.xml中添加如下SQL:

<!--查询首页最新博客列表信息-->
<resultMap id="firstPageBlog" type="com.star.queryvo.FirstPageBlog">
    <id property="id" column="id"/>
    <result property="title" column="title"/>
    <result property="firstPicture" column="first_picture"/>
    <result property="views" column="views"/>
    <result property="commentCount" column="comment_count"/>
    <result property="createTime" column="create_time"/>
    <result property="updateTime" column="update_time"/>
    <result property="description" column="description"/>
    <result property="typeName" column="name"/>
    <result property="nickname" column="nickname"/>
    <result property="avatar" column="avatar"/>
</resultMap>
<select id="getFirstPageBlog" resultMap="firstPageBlog">
    select b.id,b.title,b.first_picture, b.views, b.comment_count,b.create_time,b.update_time,b.description,
    t.name ,
    u.nickname, u.avatar
    from myblog.t_blog b, myblog.t_type t,myblog.t_user u
    where b.type_id = t.id and  u.id = b.user_id order by b.create_time desc
</select>

<!--查询推荐文章-->
<select id="getAllRecommendBlog" resultType="com.star.queryvo.RecommendBlog">
    select * from myblog.t_blog where t_blog.recommend = true order by t_blog.create_time desc limit 4;
</select>

<!--搜索文章-->
<select id="getSearchBlog" resultMap="firstPageBlog">
    <bind name="pattern" value="'%' + query + '%'" />
    select b.id,b.title,b.first_picture, b.views,b.comment_count,b.update_time,b.description,
    t.name ,
    u.nickname, u.avatar
    from myblog.t_blog b, myblog.t_type t,myblog.t_user u
    where b.type_id = t.id and  u.id = b.user_id and (b.title like #{pattern} or b.content like  #{pattern})
    order by b.update_time desc
</select>

<!--统计博客信息-->
<select id="getBlogTotal" resultType="Integer">
    select count(*) from myblog.t_blog
</select>
<select id="getBlogViewTotal" resultType="Integer">
    select coalesce (sum(views),0) from myblog.t_blog
</select>
<select id="getBlogCommentTotal" resultType="Integer">
    select count(*) from myblog.t_comment
</select>
<select id="getBlogMessageTotal" resultType="Integer">
    select count(*) from myblog.t_message
</select>

讲解:

查询首页最新博客列表信息和查询推荐文章都是前面提到过的知识点,搜索文章也在博客管理里面有讲解过,用的是模糊查询,这里说一统计访问总数的SQL,在上一版的代码中,用的是:select sum(views) from myblog.t_blog,这里用的是:select coalesce (sum(views),0) from myblog.t_blog,在上一版中,当sum求和返回为null时,是会报空指针异常的,这里用coalesce (sum(views),0),打当sum求和为null时赋值为0,就能解决这个问题

4. 业务层

业务层接口

在BlogService接口中定义以下接口

//查询首页最新博客列表信息
List<FirstPageBlog> getAllFirstPageBlog();

//查询首页最新推荐信息
List<RecommendBlog> getRecommendedBlog();

//搜索博客列表
List<FirstPageBlog> getSearchBlog(String query);

//统计博客总数
Integer getBlogTotal();

//统计访问总数
Integer getBlogViewTotal();

//统计评论总数
Integer getBlogCommentTotal();

//统计留言总数
Integer getBlogMessageTotal();
接口实现类

在BlogServiceImpl接口实现类中添加:

//查询首页最新博客列表信息
@Override
public List<FirstPageBlog> getAllFirstPageBlog() {
    return blogDao.getFirstPageBlog();
}

//查询首页最新推荐信息
@Override
public List<RecommendBlog> getRecommendedBlog() {
    List<RecommendBlog> allRecommendBlog = blogDao.getAllRecommendBlog();
    return allRecommendBlog;
}

//搜索博客列表
@Override
public List<FirstPageBlog> getSearchBlog(String query) {
    return blogDao.getSearchBlog(query);
}

//统计博客总数
@Override
public Integer getBlogTotal() {
    return blogDao.getBlogTotal();
}

//统计访问总数
@Override
public Integer getBlogViewTotal() {
    return blogDao.getBlogViewTotal();
}

//统计评论总数
@Override
public Integer getBlogCommentTotal() {
    return blogDao.getBlogCommentTotal();
}

//统计留言总数
@Override
public Integer getBlogMessageTotal() {
    return blogDao.getBlogMessageTotal();
}

5. 控制器

在controller包下创建IndexController类,根据前面的功能分析,编写如下代码:

package com.star.controller;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.star.queryvo.FirstPageBlog;
import com.star.queryvo.RecommendBlog;
import com.star.service.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.List;

/**
 * @Description: 首页控制器
 * @Date: Created in 21:01 2020/5/20
 * @Author: ONESTAR
 * @QQ群: 530311074
 * @URL: https://onestar.newstar.net.cn/
 */
@Controller
public class IndexController {

    @Autowired
    private BlogService blogService;

    //分页查询博客列表
    @GetMapping("/")
    public String index(Model model, @RequestParam(defaultValue = "1",value = "pageNum") Integer pageNum, RedirectAttributes attributes){
        PageHelper.startPage(pageNum,10);
        List<FirstPageBlog> allFirstPageBlog = blogService.getAllFirstPageBlog();
        List<RecommendBlog> recommendedBlog = blogService.getRecommendedBlog();

        PageInfo<FirstPageBlog> pageInfo = new PageInfo<>(allFirstPageBlog);
        System.out.println("pageInfo:" +pageInfo);
        model.addAttribute("pageInfo",pageInfo);
        model.addAttribute("recommendedBlogs", recommendedBlog);
        return "index";
    }

    //搜索博客
    @PostMapping("/search")
    public String search(Model model,
                         @RequestParam(defaultValue = "1", value = "pageNum") Integer pageNum,
                         @RequestParam String query) {
        PageHelper.startPage(pageNum, 1000);
        List<FirstPageBlog> searchBlog = blogService.getSearchBlog(query);

        PageInfo<FirstPageBlog> pageInfo = new PageInfo<>(searchBlog);
        model.addAttribute("pageInfo", pageInfo);
        model.addAttribute("query", query);
        return "search";
    }

    //博客信息统计
    @GetMapping("/footer/blogmessage")
    public String blogMessage(Model model){
        int blogTotal = blogService.getBlogTotal();
        int blogViewTotal = blogService.getBlogViewTotal();
        int blogCommentTotal = blogService.getBlogCommentTotal();
        int blogMessageTotal = blogService.getBlogMessageTotal();

        model.addAttribute("blogTotal",blogTotal);
        model.addAttribute("blogViewTotal",blogViewTotal);
        model.addAttribute("blogCommentTotal",blogCommentTotal);
        model.addAttribute("blogMessageTotal",blogMessageTotal);
        return "index :: blogMessage";
    }
}

6. 前后端交互

  • 最新推荐:
<div class="m-margin-tb-tiny four wide column" th:each="blog : ${recommendedBlogs}">
    <a href="#" class="class_outer" th:href="@{/blog/{id}(id=${blog.id})}" target="_blank">
        <img src="../static/images/backimg1.jpg" th:src="@{${blog.firstPicture}}"  alt="" class="ui rounded image">
        <span class="class_cover" >
             <h4 class="m-font-size-blog-text m-margin-tb-tiny" th:text="${blog.title}">大圣,此去欲何?</h4>
         </span>
    </a>
</div>
  • 文章列表
<div class="ui padded segment m-padded-tb-large m-opacity" th:each="blog : ${pageInfo.list}">
    <div class="ui large aligned mobile reversed stackable grid">
        <!--博文信息-->
        <div class="eleven wide column ">
            <h3 class="ui header" ><a href="#" th:href="@{/blog/{id}(id=${blog.id})}" target="_blank" class="m-black m-title-font" th:text="${blog.title}">大圣,此去欲何?</a></h3>
            <p class="m-text m-margin-top-max" th:text="|${blog.description}......|">戴上金箍,没法爱你;放下金箍,没法保护你。我知道上天不会给我第二次机会,曾经我们说好的永远,原来也仅仅只有,十二画,而已。“大圣,此去欲何?”“踏南天,碎凌霄。”“若一去不回……”“便一去不回” 其实很多时候,我们都是有机会的,最后真正放弃的,是我们自己。......</p>
            <div class="ui m-margin-top-max grid">
                <div class="eleven wide column">
                    <div class="ui mini horizontal link list">
                        <div class="item">
                            <img src="../static/images/me.jpg" th:src="@{${blog.avatar}}"  alt="" class="ui avatar image">
                            <div class="content"><a href="#" th:href="@{/about}" target="_blank" class="header" th:text="${blog.nickname}" >oneStar</a></div>
                        </div>
                        <div class="item">
                            <i class="calendar icon"></i><span th:text="${#dates.format(blog.createTime,'yyyy-MM-dd')}">2020-01-01</span>
                        </div>
                        <div class="item">
                            <i class="eye icon"></i> <span th:text="${blog.views}">2222</span>
                        </div>
                        <div class="item">
                            <i class="comment outline icon"></i> <span th:text="${blog.commentCount}">2222</span>
                        </div>
                    </div>
                </div>
                <div class="right aligned five wide column">
                    <a href="#" target="_blank" class="ui teal basic label m-padded-tiny m-text-thin" th:text="${blog.typeName}">好文</a>
                </div>
            </div>
        </div>
        <!--博文图片-->
        <div class="five wide column">
            <a href="#" th:href="@{/blog/{id}(id=${blog.id})}" target="_blank">
                <img src="../static/images/backimg1.jpg" th:src="@{${blog.firstPicture}}"  alt="" class="ui rounded image">
            </a>
        </div>

    </div>
</div>
  • 分页显示文章列
<div class="ui bottom attached segment m-opacity stackable grid">
    <div class="three wide column" align="center">
        <a class="item" th:href="@{/(pageNum=${pageInfo.hasPreviousPage}?${pageInfo.prePage}:1)}" th:unless="${pageInfo.isFirstPage}">上一页</a>
    </div>

    <div class="ten wide column" align="center">
        <p> <span th:text="${pageInfo.pageNum}"></span> / <span th:text="${pageInfo.pages}"></span> </p>
    </div>

    <div class="three wide column" align="center">
        <a class="item" th:href="@{/(pageNum=${pageInfo.hasNextPage}?${pageInfo.nextPage}:${pageInfo.pages})}" th:unless="${pageInfo.isLastPage}">下一页</a>
    </div>
</div>
  • 搜索博客
<form name="search" action="#" th:action="@{/search}" method="post" target="_blank">
    <div class="ui icon transparent input m-margin-tb-tiny" style="color: white">
        <input style="color: white" type="text" name="query" placeholder="Search...." th:value="${query}">
        <i onclick="document.forms['search'].submit()" class="search link icon"></i>
    </div>
</form>
  • 统计博客信息

HTML:

<div id="blog-message">
    <div class="ui inverted link list" style="align-content: center;margin-top: 10px" th:fragment="blogMessage">
        <div class="m-text-thin" style="text-align: left;margin-left: 75px;">
            文章总数: <h2 class="ui orange header m-inline-block m-margin-top-null" style="font-size:medium;" th:text="${blogTotal}"> 14 </h2></div>
        <div class="m-text-thin" style="text-align: left;margin-left: 75px">
            访问总数: <h2 class="ui orange header m-inline-block m-margin-top-null" style="font-size:medium;" th:text="${blogViewTotal}"> 14 </h2></div>
        <div class="m-text-thin" style="text-align: left;margin-left: 75px">
            评论总数: <h2 class="ui orange header m-inline-block m-margin-top-null" style="font-size:medium;" th:text="${blogCommentTotal}"> 14 </h2></div>
        <div class="m-text-thin" style="text-align: left;margin-left: 75px">
            留言总数: <h2 class="ui orange header m-inline-block m-margin-top-null" style="font-size:medium;" th:text="${blogMessageTotal}"> 14 </h2></div>
    </div>
</div>

JS:

$('#blog-message').load(/*[[@{/footer/blogmessage}]]*/"/footer/blogmessage");

7. 运行访问

运行项目,访问 http://localhost:8080/, 在后台添加测试文章后能在前端页面查看,并且在底部栏可以查看本站的信息

image

image

至此,SpringBoot搭建个人博客的博客首页显示开发完成,下一篇将讲述博客详情页面显示

【点关注,不迷路,欢迎持续关注本站】


image