基于SpringBoot + Vue的个人博客系统03——标签和分类接口

简介

有了文章的CRUD,现在来写文章的标签和分类相关的接口

标签

首先我们要明确需求,现在我们需要的是:

  • 查询出全部的标签
  • 根据标签找到对应的文章列表

1、新建 TagController

package pers.qianyucc.qblog.controller;

import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.web.bind.annotation.*;
import pers.qianyucc.qblog.model.vo.*;
import pers.qianyucc.qblog.service.*;

import java.util.*;

@RestController
public class TagController {
    @Autowired
    private TagService tagService;

    @ApiOperation("获取所有的标签")
    @GetMapping("/tags")
    public Results<Set<String>> getTags() {
        return Results.ok(tagService.getAllTags());
    }
}

说明:

  • 这部分的业务还是和文章有关的,也可以写到 ArticleController 中,这里为了防止 ArticleController 过于臃肿就新建了一个 Controller

2、新建 TagService

package pers.qianyucc.qblog.service;

import com.baomidou.mybatisplus.core.conditions.query.*;
import lombok.extern.slf4j.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import pers.qianyucc.qblog.dao.*;
import pers.qianyucc.qblog.model.entity.*;

import java.util.*;
import java.util.stream.*;

@Service
@Slf4j
public class TagService {
    @Autowired
    private ArticleMapper articleMapper;

    /***
     * 查询所有标签
     * @return 标签集合
     */
    public Set<String> getAllTags() {
        QueryWrapper<ArticlePO> wrapper = new QueryWrapper<>();
        wrapper.select("tags");
        List<ArticlePO> articles = articleMapper.selectList(wrapper);
        Set<String> tags = articles.stream()
                .map(ArticlePO::getTags)
                .flatMap(s -> Arrays.stream(s.split(",")))
                .collect(Collectors.toSet());
        log.info("tags : {}", tags);
        return tags.isEmpty() ? new HashSet<>() : tags;
    }
}

说明:

  • Set 用于去重
  • flatMap 方法可以将多个流合并成一个流

3、使用 Swagger 测试接口

测试查询所有标签接口

4、在 TagController 中编写根据标签查询的 api

@GetMapping("/tag/{name}")
@ApiOperation("根据标签查询文章集合")
@ApiImplicitParam(name = "name", value = "标签名称", required = true, dataType = "String", paramType = "path")
public Results<PageVO<ArticleVO>> getArticle(
        @PathVariable("name") String tagName,
        @ApiParam("页码")
        @RequestParam(required = false, defaultValue = "1") Integer page,
        @ApiParam("每页存放的记录数")
        @RequestParam(required = false, defaultValue = "5") Integer limit) {
    PageVO<ArticleVO> pv = tagService.getArticleByTag(tagName, page, limit);
    return Results.ok(pv);
}

说明:

  • 标签名称使用路径参数
  • 为适应文章数量比较多的情况,这里提供分页功能

5、对应 TagService 如下

public PageVO<ArticleVO> getArticleByTag(String tagName, Integer page, Integer limit) {
    QueryWrapper<ArticlePO> wrapper = new QueryWrapper<>();
    wrapper.select(ArticlePO.class, i -> !"content".equals(i.getColumn()))
            .like("tags", tagName);
    Page<ArticlePO> res = articleMapper.selectPage(new Page<>(page, limit), wrapper);
    List<ArticleVO> articleVOS = res.getRecords().stream()
            .map(ArticleVO::fromArticlePO)
            .collect(Collectors.toList());
    PageVO<ArticleVO> pv = PageVO.<ArticleVO>builder()
            .records(articleVOS.isEmpty() ? new ArrayList<>() : articleVOS)
            .total(res.getTotal())
            .current(res.getCurrent())
            .size(res.getSize())
            .build();
    return pv;
}

说明:

  • 因为查找文章列表的时候通常就不需要看到文章内容,这里排除 content 字段
  • 这里其实使用 like 来查询时存在问题的,在一个标签内容包含另一个标签的时候会出现问题,但是由于出现这种情况的概率不大,这里就使用 like 了,有更好想法的朋友可以自行优化

6、测试结果

测试根据标签获取的接口

分类

由于分类和标签的需求基本相同,这里就直接上代码了

1、CategoryController

package pers.qianyucc.qblog.controller;

import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.web.bind.annotation.*;
import pers.qianyucc.qblog.model.vo.*;
import pers.qianyucc.qblog.service.*;

import java.util.*;

@RestController
public class CategoryController {
    @Autowired
    private CategoryService categoryService;

    @ApiOperation("获取所有的文章分类以及对应分类文章数")
    @GetMapping("/categories")
    public Results<List<CategoryVO>> getCategories() {
        return Results.ok(categoryService.getAllCategories());
    }

    @GetMapping("/category/{name}")
    @ApiOperation("根据分类查询文章集合")
    @ApiImplicitParam(name = "name", value = "分类名称", required = true, dataType = "String", paramType = "path")
    public Results<PageVO<ArticleVO>> getArticle(
            @PathVariable("name") String categoryName,
            @ApiParam("页码")
            @RequestParam(required = false, defaultValue = "1") Integer page,
            @ApiParam("每页存放的记录数")
            @RequestParam(required = false, defaultValue = "5") Integer limit) {
        PageVO<ArticleVO> pv = categoryService.getArticleByCategory(categoryName, page, limit);
        return Results.ok(pv);
    }
}

2、CategoryService

package pers.qianyucc.qblog.service;

import com.baomidou.mybatisplus.core.conditions.query.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import pers.qianyucc.qblog.dao.*;
import pers.qianyucc.qblog.model.entity.*;
import pers.qianyucc.qblog.model.vo.*;

import java.util.*;
import java.util.stream.*;

@Service
public class CategoryService {
    @Autowired
    private ArticleMapper articleMapper;

    public List<CategoryVO> getAllCategories() {
        QueryWrapper<ArticlePO> wrapper = new QueryWrapper<>();
        wrapper.select("category as `name`", "count(1) as `count`").groupBy("category");
        List<CategoryVO> list = articleMapper.selectMaps(wrapper).stream()
                .map(CategoryVO::fromMap)
                .collect(Collectors.toList());
        return list.isEmpty() ? new ArrayList<>() : list;
    }

    public PageVO<ArticleVO> getArticleByCategory(String categoryName, Integer page, Integer limit) {
        QueryWrapper<ArticlePO> wrapper = new QueryWrapper<>();
        wrapper.select(ArticlePO.class, i -> !"content".equals(i.getColumn()))
                .eq("category", categoryName);
        Page<ArticlePO> res = articleMapper.selectPage(new Page<>(page, limit), wrapper);
        List<ArticleVO> articleVOS = res.getRecords().stream()
                .map(ArticleVO::fromArticlePO)
                .collect(Collectors.toList());
        PageVO<ArticleVO> pv = PageVO.<ArticleVO>builder()
                .records(articleVOS.isEmpty() ? new ArrayList<>() : articleVOS)
                .total(res.getTotal())
                .current(res.getCurrent())
                .size(res.getSize())
                .build();
        return pv;
    }
}

在查询所有分类的时候我们要连带查询出分类对应的文章数,所以封装了一个 CategoryVO 类,这里也可以使用 Map ,但是个人感觉封装对象的可读性会更好

CategoryVO

package pers.qianyucc.qblog.model.vo;

import lombok.*;

import java.util.*;

@Data
@Builder
public class CategoryVO {
    private String name;
    private String count;

    public static CategoryVO fromMap(Map<String, Object> map) {
        return new CategoryVO.Converter().convertToVO(map);
    }

    private static class Converter implements IConverter<Map<String,Object>, CategoryVO> {
        @Override
        public CategoryVO convertToVO(Map<String, Object> map) {
            CategoryVO vo = CategoryVO.builder()
                    .name(String.valueOf(map.get("name")))
                    .count(String.valueOf(map.get("count")))
                    .build();
            return vo;
        }
    }
}

说明:

  • 这里转成 String 的时候尽量使用 String.valueOf 而不要使用强制类型转换,因为包装类直接转成 String 类会报错

参考:https://gitee.com/qianyucc/QBlog2/tree/v-4.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值