4.【SpringBoot3】文章管理接口开发

序言

在文章管理模块,有以下接口需要开发:

  • 新增文章
  • 文章列表(条件分页)
  • 获取文章详情
  • 更新文章
  • 删除文章

数据库表字段和实体类属性:

4.【SpringBoot3】文章管理接口开发_第1张图片

1. 新增文章

需求分析

当用户点击左侧菜单中的“文章管理”后,页面主区域展示该用户的所有文章列表:

4.【SpringBoot3】文章管理接口开发_第2张图片

页面右上角有“发布文章”按钮,当用户点击该按钮后会弹出一个抽屉式界面,里面有一个表单,用户需要填写“文章标题”、“文章分类”,选择文章封面,编辑文章内容。最后点击“发布”或“草稿”按钮,以访问后台接口完成文章的新增。

4.【SpringBoot3】文章管理接口开发_第3张图片

接口文档

4.【SpringBoot3】文章管理接口开发_第4张图片

1.1 新增文章基本代码编写

接口实现思路

Controller 中用于新增文章的方法参数需要接收请求体中 json 格式的数据,所以添加 @RequestBody 注解。方法内部调用 Service 层的方法完成新增文章,Mapper 层执行相应的 SQL。
4.【SpringBoot3】文章管理接口开发_第5张图片

根据前几篇文章的知识,可以写出该部分的基本逻辑,并且对 Article 实体类中除 state 之外的属性进行参数校验:

(1) Controller

//新增文章
@PostMapping
public Result add(@RequestBody @Validated Article article){
    articleService.add(article);
    return Result.success();
}

(2) Service

//Service接口中
//新增文章
void add(Article article);

//Service实现类中
//新增文章
@Override
public void add(Article article) {
    article.setCreateTime(LocalDateTime.now());
    article.setUpdateTime(LocalDateTime.now());
    Map<String, Object> map =  ThreadLocalUtil.get();
    Integer userId = (Integer) map.get("id");
    article.setCreateUser(userId);
    articleMapper.add(article);
}

(3) Mapper

//新增文章
@Insert("insert into article(title, content, cover_img, state, category_id, create_user, create_time, update_time) " +
        "values(#{title}, #{content}, #{coverImg}, #{state}, #{categoryId}, #{createUser}, #{createTime}, #{updateTime})")
void add(Article article);

(4) Article 实体类

@Data
public class Article {
    private Integer id;//主键ID
    @NotEmpty
    @Pattern(regexp = "^\\S{1,10}$")
    private String title;//文章标题
    @NotEmpty
    private String content;//文章内容
    @NotEmpty
    @URL
    private String coverImg;//封面图像在服务器上的地址
    private String state;//发布状态 已发布|草稿
    @NotNull
    private Integer categoryId;//文章所属的分类id
    private Integer createUser;//创建人id,用户只能操作自己创建的文章
    private LocalDateTime createTime;//创建时间
    private LocalDateTime updateTime;//更新时间
}

postman 测试:

4.【SpringBoot3】文章管理接口开发_第6张图片

在这里插入图片描述

1.2 Validation 自定义校验

对于 Article 实体类中的 state 属性,前面所学的知识已经不能完成对它的校验了。此时,需要借助 Validation 完成自定义校验。用自定义注解完成 state 属性校验的过程如下:

  • 自定义注解
    自定义的注解有要求,里面必须提供三个属性 message、groups、Payload,定义好该注解后,将来就用它来完成 Article 实体类中 state 属性的校验
  • 自定义校验数据的类 StateValidation 实现 ConstraintValidator 接口
  • 在需要校验的地方使用自定义注解

(1) 自定义注解

新建注解:

4.【SpringBoot3】文章管理接口开发_第7张图片

4.【SpringBoot3】文章管理接口开发_第8张图片

注解文件中的内容不会写怎么办?打开一个现成的照着写。比如 @NotNull:

4.【SpringBoot3】文章管理接口开发_第9张图片

NotNull 上组合了一些注解,内部有三个必要的属性,先复制过来,并改动一下:

@Documented//元注解,标识当前注解将来可以被抽取到帮助文档中
@Target({ElementType.FIELD})//元注解,标识当前注解将来可以用在哪些地方:类、属性(FIELD)、方法、参数
@Retention(RetentionPolicy.RUNTIME)//元注解,标识当前注解保留到哪个阶段:源码阶段、编译阶段、运行阶段
@Constraint(validatedBy = {StateValidation.class})//指定为该注解提供校验规则的类
// @Repeatable(NotEmpty.List.class)//不需要
public @interface State {
    //提供校验失败后的提示信息
    String message() default "state参数的值只能是已发布或者草稿";

    //指定分组
    Class<?>[] groups() default {};

    //负载,用于获取当前注解的附加信息
    //虽然该属性是必须的,但一般用不到,所以可以先不关注
    Class<? extends Payload>[] payload() default {};
}

(2) 自定义校验数据的类

4.【SpringBoot3】文章管理接口开发_第10张图片

//ConstraintValidator<给哪个注解提供校验规则,校验的数据类型>
public class StateValidation implements ConstraintValidator<State,String> {
    /**
     *
     * @param s:将来要校验的数据
     * @param context
     * @return 返回true校验通过,返回false校验不通过
     */
    @Override
    public boolean isValid(String s, ConstraintValidatorContext context) {
        //提供校验规则
        if (s == null){
            return false;
        }
        if (s.equals("已发布") || s.equals("草稿")){
            return true;
        }
        //其他情况都校验不通过:空串、其他不符合要求的字符串
        return false;
    }
}

String s = null 表示声明了字符串变量 s,但是未赋值(s 并未指向一个地址),调用 equals 函数会触发空指针异常,因为没有什么可以比较的数据。
String s = "" 表示声明了字符串变量 s,且赋值为空串(s 指向了一个地址,虽然这个地址没什么东西),s.equals(“”) 的结果为 true。

(3) 使用自定义的 State 注解

4.【SpringBoot3】文章管理接口开发_第11张图片

postman 测试:

state 不传入或传入的不是“已发布”或“草稿”,测试不通过:

4.【SpringBoot3】文章管理接口开发_第12张图片

state 传入“已发布”或“草稿”,测试通过:

4.【SpringBoot3】文章管理接口开发_第13张图片

在这里插入图片描述

2. 文章列表(条件分页)

需求分析

当用户点击左侧菜单中的“文章管理”后,页面主区域会以列表形式展示该用户已创建的文章信息,用户还可以根据文章分类和发布状态去搜索文章。页面底部展示了一个分页条,包括总记录数,还有一些可选择项,用户可以在此选择每页展示的条数、当前要查询的页码等。当用户点击了搜索、上一页、下一页、页码,或修改了每页显示的条数后,都需要访问后台接口,以查询满足条件的当前页数据,最终在列表中展示。

4.【SpringBoot3】文章管理接口开发_第14张图片

接口文档

4.【SpringBoot3】文章管理接口开发_第15张图片
接口实现思路

在 Controller 层,用于获取文章列表的 list() 方法返回值类型是 Result> ,即:在响应给浏览器的结果中,data 属性的类型是PageBean

。其中,PageBean 是一个自定义的类,用于封装分页查询结果,该类中至少包含 total(总条数) 和 items(当前页面的数据集合) 两个属性,将来后台经过一顿操作,将查询好的数据封装到 PageBean 对象中,响应给浏览器的格式就能满足接口文档的要求了。

4.【SpringBoot3】文章管理接口开发_第16张图片

4.【SpringBoot3】文章管理接口开发_第17张图片

另外,接口文档中注明了categoryId 和 state 不是必须的,所以前端可以不传这两个参数,因此后端方法对应的参数上要用 @RequestParam(required = false) 注解。

4.【SpringBoot3】文章管理接口开发_第18张图片

然后,在方法内部,调用 Service 中的方法完成查询就可以了。Service 层的方法内部首先要构建一个 PageBean 对象来封装查询到的数据,然后借助 mybatis 提供的 PageHelper 插件完成分页查询。要想利用 PageHelper 完成分页查询,只需在调用 Mapper 层的方法执行 SQL 之前开启分页查询即可。

4.【SpringBoot3】文章管理接口开发_第19张图片

Mapper 层也需要声明对应的方法来执行 SQL。但是要注意,现在的任务是条件搜索,对于某些条件,前端有可能传,也有可能不传,于是就需要用 mybatis 的动态 SQL 技术来编写 SQL。写动态 SQL 使用 mapper 映射配置文件比较方便,因此还需要创建 mapper 映射配置文件。

4.【SpringBoot3】文章管理接口开发_第20张图片

代码实现

导入 PageHelper 坐标

<dependency>
  <groupId>com.github.pagehelpergroupId>
  <artifactId>pagehelper-spring-boot-starterartifactId>
  <version>1.4.6version>
dependency>

PageBean 实体类

//分页返回结果对象
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean <T>{
    private Long total;//总条数
    private List<T> items;//当前页数据集合
}

Controller 层:

//文章列表(条件分页)
@GetMapping
public Result<PageBean<Article>> list(
        Integer pageNum,
        Integer pageSize,
        @RequestParam(required = false) String categoryId,
        @RequestParam(required = false) String state
){
    PageBean<Article> pg = articleService.list(pageNum, pageSize, categoryId, state);
    return Result.success(pg);
}

Service 层:

//ArticleService接口
//文章列表(条件分页)
PageBean<Article> list(Integer pageNum, Integer pageSize, String categoryId, String state);

//ArticleService实现类
//文章列表(条件分页)
@Override
public PageBean<Article> list(Integer pageNum, Integer pageSize, String categoryId, String state) {
    //创建PageBean对象
    PageBean<Article> pb = new PageBean<>();
    //开启分页查询
    PageHelper.startPage(pageNum, pageSize);
    //调用mapper完成查询
    Map<String,Object> map = ThreadLocalUtil.get();
    Integer userId = (Integer) map.get("id");
    //看下面关于参数的解释
    List<Article> al = articleMapper.list(userId, categoryId, state);
    //Page提供了方法,可以获取PageHelper分页查询后得到的总记录数和当前页数据
    //多态的特性,如果不强转,不允许父类对象调用子类中独有方法
    Page<Article> ap = (Page<Article>) al;
    //把数据填充到PageBean对象中
    pb.setTotal(ap.getTotal());
    pb.setItems(ap.getResult());
    return pb;
}

在调用 Mapper 层接口完成查询时,需要传递哪些参数?首先,文章所属的分类 categoryId、发布状态 state 两个条件肯定要传入。当前页码 pageNum 和每页条数 pageSize 是不需要传的,因为有了 PageHelper 后,它会自动将 pageNum 和 pageSize 拼接到 SQL 后面,加上一个 limit,从而完成分页查询。那么除了这两个条件之外,还需要其他参数吗?Article 实体类中有个 createUser,用于限制用户只操作自己创建的文章。因此,还要传递一个当前用户的 id 作为参数。

Mapper 层:

不建议直接在方法上用注解写动态 SQL,因为会非常麻烦。此处使用映射配置文件实现动态 SQL,下面首先准备映射设配置文件:

在 resources 下创建与 mapper 接口所在包结构相同的目录文件夹,新建映射配置文件,该映射配置文件必须与对应接口处在同一目录下,同时文件名称与对应接口一致。

4.【SpringBoot3】文章管理接口开发_第21张图片

ArticleMapper.java

//文章列表(条件分页)
List<Article> list(Integer userId, String categoryId, String state);

ArticleMapper.xml

<?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">
<!--名称空间的值必须是对应接口的全类名 输入ArticleMapper后,再Alt+Ctrl+空格会有提示-->
<mapper namespace="com.itheima.mapper.ArticleMapper">
    <!--编写动态SQL-->

    <!--id是mapper中对应方法的名称-->
    <!--多条数据对应的实体类类型-->
    <select id="list" resultType="com.itheima.pojo.Article">
        select * from article
        # where标签的好处:如果有条件就加where关键字,否则不加,并且还可以去掉第一个条件前面的and
        # 对条件参数进行判断,id一定存在,所以不用判断
        <where>
            <if test="categoryId!=null">
                category_id=#{categoryId}
            </if>

            <if test="state!=null">
                and state=#{state}
            </if>
            and create_user=#{userId}
        </where>
    </select>
    
</mapper>

注意:在 where 标签内部写注释会导致它不能去掉第一个条件前面的 and。

数据库中现有的文章数据:

在这里插入图片描述

postman 测试:

查询已登录用户的所有文章,每页展示 3 条数据,展示第 1 页:

4.【SpringBoot3】文章管理接口开发_第22张图片

{
    "code": 0,
    "message": "操作成功",
    "data": {
        "total": 4,
        "items": [
            {
                "id": 1,
                "title": "长沙旅游攻略",
                "content": "天安门...爱去哪去哪...",
                "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
                "state": "已发布",
                "categoryId": 9,
                "createUser": 3,
                "createTime": "2024-01-23T19:24:03",
                "updateTime": "2024-01-23T19:24:03"
            },
            {
                "id": 2,
                "title": "北京旅游攻略",
                "content": "天安门...爱去哪去哪...",
                "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
                "state": "已发布",
                "categoryId": 9,
                "createUser": 3,
                "createTime": "2024-01-23T20:35:13",
                "updateTime": "2024-01-23T20:35:13"
            },
            {
                "id": 3,
                "title": "湖南旅游攻略",
                "content": "岳麓山...爱去哪去哪...",
                "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
                "state": "草稿",
                "categoryId": 9,
                "createUser": 3,
                "createTime": "2024-01-23T20:38:11",
                "updateTime": "2024-01-23T20:38:11"
            }
        ]
    }
}

查询已登录用户的所有文章,每页展示 3 条数据,展示第 2 页:

4.【SpringBoot3】文章管理接口开发_第23张图片

{
    "code": 0,
    "message": "操作成功",
    "data": {
        "total": 4,
        "items": [
            {
                "id": 4,
                "title": "日本核污水排放",
                "content": "此处省略10000个字...",
                "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
                "state": "已发布",
                "categoryId": 5,
                "createUser": 3,
                "createTime": "2024-01-24T00:54:04",
                "updateTime": "2024-01-24T00:54:04"
            }
        ]
    }
}

查询已登录用户的类别 id 为 9 的文章,每页展示 3 条数据,展示第 1 页:

4.【SpringBoot3】文章管理接口开发_第24张图片

{
    "code": 0,
    "message": "操作成功",
    "data": {
        "total": 3,
        "items": [
            {
                "id": 1,
                "title": "长沙旅游攻略",
                "content": "天安门...爱去哪去哪...",
                "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
                "state": "已发布",
                "categoryId": 9,
                "createUser": 3,
                "createTime": "2024-01-23T19:24:03",
                "updateTime": "2024-01-23T19:24:03"
            },
            {
                "id": 2,
                "title": "北京旅游攻略",
                "content": "天安门...爱去哪去哪...",
                "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
                "state": "已发布",
                "categoryId": 9,
                "createUser": 3,
                "createTime": "2024-01-23T20:35:13",
                "updateTime": "2024-01-23T20:35:13"
            },
            {
                "id": 3,
                "title": "湖南旅游攻略",
                "content": "岳麓山...爱去哪去哪...",
                "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
                "state": "草稿",
                "categoryId": 9,
                "createUser": 3,
                "createTime": "2024-01-23T20:38:11",
                "updateTime": "2024-01-23T20:38:11"
            }
        ]
    }
}

查询已登录用户的类别 id 为 9、且已发布的文章,每页展示 3 条数据,展示第 1 页:

4.【SpringBoot3】文章管理接口开发_第25张图片

{
    "code": 0,
    "message": "操作成功",
    "data": {
        "total": 2,
        "items": [
            {
                "id": 1,
                "title": "长沙旅游攻略",
                "content": "天安门...爱去哪去哪...",
                "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
                "state": "已发布",
                "categoryId": 9,
                "createUser": 3,
                "createTime": "2024-01-23T19:24:03",
                "updateTime": "2024-01-23T19:24:03"
            },
            {
                "id": 2,
                "title": "北京旅游攻略",
                "content": "天安门...爱去哪去哪...",
                "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
                "state": "已发布",
                "categoryId": 9,
                "createUser": 3,
                "createTime": "2024-01-23T20:35:13",
                "updateTime": "2024-01-23T20:35:13"
            }
        ]
    }
}

对于后面的接口,由于所涉及的知识前面都介绍过了,所以只根据接口文档编写代码,不再过多分析。

3. 获取文章详情

接口文档

4.【SpringBoot3】文章管理接口开发_第26张图片
Controller 中:

//获取文章详情
@GetMapping("/detail")
public Result<Article> detail(Integer id){
    Article article = articleService.detail(id);
    return Result.success(article);
}

Service 中:

//Service接口
//获取文章详情
Article detail(Integer id);

//Service实现类
//获取文章详情
@Override
public Article detail(Integer id) {
    Article article = articleMapper.detail(id);
    return article;
}

Mapper 中:

//获取文章详情
@Select("select * from article where id=#{id}")
Article detail(Integer id);

postman 测试:

4.【SpringBoot3】文章管理接口开发_第27张图片

{
    "code": 0,
    "message": "操作成功",
    "data": {
        "id": 3,
        "title": "湖南旅游攻略",
        "content": "岳麓山...爱去哪去哪...",
        "coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
        "state": "草稿",
        "categoryId": 9,
        "createUser": 3,
        "createTime": "2024-01-23T20:38:11",
        "updateTime": "2024-01-23T20:38:11"
    }
}

4. 更新文章

接口文档

4.【SpringBoot3】文章管理接口开发_第28张图片
Controller 中:

//更新文章
@PutMapping
public Result update(@RequestBody @Validated(Article.Update.class) Article article){
    articleService.update(article);
    return Result.success();
}

Service 中:

//Service接口
//更新文章
void update(Article article);

//Service实现类
//更新文章
@Override
public void update(Article article) {
    article.setUpdateTime(LocalDateTime.now());
    Map<String,Object> map = ThreadLocalUtil.get();
    Integer userId = (Integer) map.get("id");
    article.setCreateUser(userId);
    articleMapper.update(article);
}

Mapper 中:

//更新文章
@Update("update article set title=#{title}, content=#{content}, cover_img=#{coverImg}, " +
        "state=#{state}, category_id=#{categoryId}, create_user=#{createUser}, update_time=#{updateTime} " +
        "where id=#{id}")
void update(Article article);

postman 测试:

4.【SpringBoot3】文章管理接口开发_第29张图片
在这里插入图片描述
在这里插入图片描述

5. 删除文章

接口文档

4.【SpringBoot3】文章管理接口开发_第30张图片
Controller 中:

//删除文章
@DeleteMapping
public Result delete(Integer id){
    articleService.delete(id);
    return Result.success();
}

Service 中:

//Service接口
//删除文章
void delete(Integer id);

//Service实现类
//删除文章
@Override
public void delete(Integer id) {
    articleMapper.delete(id);
}

Mapper 中:

//删除文章
@Delete("delete from article where id=#{id}")
void delete(Integer id);

postman 测试:

4.【SpringBoot3】文章管理接口开发_第31张图片

在这里插入图片描述
在这里插入图片描述

你可能感兴趣的:(SpringBoot,spring,boot,java,后端,文章管理系统,文章管理接口)