在一个正常的博客系统中,归档页面也是必不可少的,需要将文章以年份或者月份归档起来,让文章依次展示出来。下面将说明Halo是怎么实现文章归档的。

实现思路

使用一个类来专门存储归档数据,这个类包括的属性有年份月份年份与月份对应的文章数量年份与月份对应的文章。这样一列出来之后,那么问题就好解决多了,只需要写一个SQL语句查询就好了,然后再到Service层拼装好给视图层调用。

Archive实体类

@Data
public class Archive {

    /**
     * 年份
     */
    private String year;

    /**
     * 月份
     */
    private String month;

    /**
     * 对应的文章数
     */
    private String count;

    /**
     * 对应的文章
     */
    private List<Post> posts;
}

SQL语句

select year(post_date) as year,month(post_date) as month,count(*) as count from halo_post where post_status=0 and post_type='post' group by year(post_date),month(post_date) order by year desc,month desc

注意:上面的post_status字段为文章状态,因为只需要查询已经发布的文章,所以条件为0,post_type为文章类型,因为Halo里面的文章是分为博文和页面的,所以条件让他为post。

实现代码

因为Halo使用的持久层是Spring-Data-Jpa,所以我们需要在Repository里面把查询的语句写好。

PostRepository:

public interface PostRepository extends JpaRepository<Post,Long>{


    /**
     * 查询文章归档信息 根据年份和月份
     *
     * @return List<Object[]></>
     */
    @Query(value = "select year(post_date) as year,month(post_date) as month,count(*) as count from halo_post where post_status=0 and post_type='post' group by year(post_date),month(post_date) order by year desc,month desc",nativeQuery = true)
    List<Object[]> findPostGroupByYearAndMonth();


    /**
     * 根据年份和月份查询文章
     *
     * @param year year
     * @param month month
     * @return List<Post></>
     */
    @Query(value = "select *,year(post_date) as year,month(post_date) as month from halo_post where post_status=0 and post_type='post' and year(post_date)=:year and month(post_date)=:month order by post_date desc",nativeQuery = true)
    List<Post> findPostByYearAndMonth(@Param("year") String year,@Param("month") String month);
}

这里需要注意的是,上面的findPostGroupByYearAndMonth方法是用来根据年份月份归档的,也就是说查询出来有哪些年份和月份,所查询出来的字段有year,month,count,那么这样的话,Archive里面的yearmonthcount就有数据了,那么posts咋整?所以还需要一个方法来根据年份和月份查询文章,那就是下面的findPostByYearAndMonth方法。

PostService:

@Service
public class PostServiceImpl implements PostService {

    @Autowired
    private PostRepository postRepository;

    /**
     * 查询归档信息 根据年份和月份
     *
     * @return List
     */
    @Override
    public List<Archive> findPostGroupByYearAndMonth() {
        //把归档数据查询出来,不包含文章数据
        List<Object[]> objects = postRepository.findPostGroupByYearAndMonth();
        //Archive的集合
        List<Archive> archives = new ArrayList<>();
        Archive archive = null;
        //遍历objects集合,分别根据年份月份查询出对应的文章,保存在archive实体,最后在循环结束时将archive添加到archives集合。
        for (Object[] obj : objects) {
            archive = new Archive();
            archive.setYear(obj[0].toString());
            archive.setMonth(obj[1].toString());
            archive.setCount(obj[2].toString());
            archive.setPosts(this.findPostByYearAndMonth(obj[0].toString(), obj[1].toString()));
            archives.add(archive);
        }
        return archives;
    }

    /**
     * 根据年份和月份查询文章
     *
     * @param year  year
     * @param month month
     * @return list
     */
    @Override
    public List<Post> findPostByYearAndMonth(String year, String month) {
        return postRepository.findPostByYearAndMonth(year, month);
    }
}

上面业务层的方法就已经把整个归档数据拼装好了,现在只需要在视图控制器里面调用就可以了。

archives.ftl:

最后就是需要在页面上显示了,页面接收到视图控制器发来的数据之后,将archives集合循环出来就行了。

<#list archivesLess as archives>
    <div class="listing-title">${archive.year}</div>
    <ul class="listing">
        <#list archive.posts?sort_by("postDate")?reverse as post>
            <div class="listing-item">
                <div class="listing-post">
                    <a href="/archives/${post.postUrl}" >${post.postTitle}</a>
                    <div class="post-time">
                        <span class="date">${post.postDate?string("yyyy-MM-dd")}</span>
                    </div>
                </div>
            </div>
        </#list>
    </ul>
</#list>

结尾

这只是一种实现思路,事实证明这是可行的,并且在各个主题上都是可以完美显示的,我之前也有看到过有人用JDK8的steam来做,不过对steam不是很了解,如果你们有更好的实现方法的话,欢迎提出来。