区块链技术博客
www.b2bchain.cn

19.博客系统项目开发之分类模块功能实现求职学习资料

本文介绍了19.博客系统项目开发之分类模块功能实现求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

Spring Boot 博客系统项目开发之分类模块功能实现

19.博客系统项目开发之分类模块功能实现

本项目的开源仓库地址为:https://github.com/ZHENFENG13/My-Blog

除 My-Blog 项目外,我也在维护另外一个开源项目,仓库地址为:https://github.com/newbee-ltd/newbee-mall

19.博客系统项目开发之分类模块功能实现

感兴趣的朋友可以去关注一下。

文章总览

前文已经实现了用户的登录及权限验证模块,接下来将会继续完善后台功能,开发博客系统的文章相关模块,本节实验将会对博客的分类模块进行介绍和功能开发及完善。

知识点

  • 分类模块介绍
  • 分类模块表结构设计及接口实现
  • 分类模块页面设计及编码
  • 分类功能测试

环境

  • JDK 1.8 或者更高版本
  • Spring Boot 2.1.0-RELEASE
  • Maven 3+

分类模块简介

在博客系统中,分类模块的设计是不可缺少的,我们在各大博客网站中都能够看到这个模块设计,在浏览文章的过程中,我们也会挑选出我们感兴趣类别中的文章进行阅读,比如看了一篇架构的文章,觉得不错,那么就想接着去看这个类别下的其他文章,或者你偏爱前端类别下的内容,那就可以针对性的浏览所有前端类别下的文章,因此对博文进行归类是十分必要的。

分类是通过比较事物之间的相似性,把具有某些共同点或相似特征的事物归属于一个不确定集合的逻辑方法,而对应的,我们可以说分类别的作用是使一个大集合中的内容条理清楚,层次分明,接下来是功能开发的讲解。

表结构设计及 Mapper 文件自动生成

表结构设计

在进行接口设计和具体的功能实现前,首先将表结构确定下来,每篇文章都会被归类到一个类别下,一个类别下会有多篇文章,分类实体与文章实体的关系是一对多的关系,因此在表结构设计时,在文章表中设置一个分类关联字段即可,分类表只需要将分类相关的字段定义好,分类实体与文章实体的关系交给文章表来维护即可(后续讲到文章表时再介绍),分类表的 SQL 设计如下,直接执行如下 SQL 语句即可:

USE `my_blog_db`;  /*Table structure for table `tb_blog_category` */  CREATE TABLE `tb_blog_category` (   `category_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '分类表主键',   `category_name` varchar(50) NOT NULL COMMENT '分类的名称',   `category_icon` varchar(50) NOT NULL COMMENT '分类的图标',   `category_rank` int(11) NOT NULL DEFAULT '1' COMMENT '分类的排序值 被使用的越多数值越大',   `is_deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 0=否 1=是',   `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',   PRIMARY KEY (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

分类表的字段以及每个字段对应的含义都在上面的 SQL 中有介绍,大家可以对照 SQL 进行理解,把表结构导入到数据库中即可,接下来我们进行编码工作。

MyBatis-Generator 插件自动生成 Mapper 文件

首先,我们使用 MyBatis-Generator 插件将该表对应的 Mapper 文件及对应的实体类和 Dao 层接口生成出来,这部分代码就不贴在文章中了,大家可以通过下载源码来查看。

需要注意的是,在代码生成后需要在 dao 层下的 BlogCategoryMapper 接口类上添加 @Mapper 注解以将其注册到 IOC 容器中以供后续调用(如果已经在主类上添加 @MapperScan 注解这一步可以省略)。其次,在 tb_blog_category 表中,我们设计了一个 is_deleted 字段,用于逻辑删除的标志位,由于 is_deleted 的字段设计,我们对表中数据的删除都是软删除,而不是真正意义的删除,只是做了一个删除标志位,如果此字段为 1 则表示已经被删除不再使用,因为是个人博客,这么做的目的主要也是为了防止误删,因此我们需要修改 Mapper 文件中的 查询语句和删除语句,将 is_deleted 条件带上,修改后的语句如下:(注:完整代码位于 resources/mapper/BlogCategoryMapper.xml)

   <select id="findCategoryList" parameterType="Map" resultMap="BaseResultMap">         select         <include refid="Base_Column_List"/>         from tb_blog_category         where is_deleted=0         order by category_rank desc,create_time desc         <if test="start!=null and limit!=null">             limit #{start},#{limit}         </if>     </select>      <select id="getTotalCategories" parameterType="Map" resultType="int">     select count(*)  from tb_blog_category     where is_deleted=0     </select>      <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">         select         <include refid="Base_Column_List"/>         from tb_blog_category         where category_id = #{categoryId,jdbcType=INTEGER} AND is_deleted = 0     </select>      <update id="deleteByPrimaryKey" parameterType="java.lang.Integer">     UPDATE tb_blog_category SET  is_deleted = 1     where category_id = #{categoryId,jdbcType=VARCHAR} AND is_deleted = 0     </update>

通过以上代码我们可以看出,在删除操作时我们并不是执行 delete 语句,而是将需要删除的这条记录的 is_deleted 字段修改为 1,这样就表示该行记录已经被执行了删除操作,那么其他的 select 查询语句就需要在查询条件中添加 is_deleted = 0 将“被删除”的记录给过滤出去,这个知识点希望大家能够理解,在后续的其他功能模块中我们也会使用软删除的设计。

分类模块接口设计及实现

接口介绍

为了让页面体验更加友好,就不采用传统的 MVC 跳转模式,一个功能一个页面,这种交互感觉有些浪费,翻页的时候,翻一页跳转一次也比较繁琐,添加或者新增的时候也要进行页面跳转,所以这些功能的实现就采用通过 Ajax 异步与后端交互数据,当使用者点击了页面上的元素,此时触发响应的 js 事件,进而通过 Ajax 的方式向后端请求数据,前端再根据后端返回的数据内容去进行响应的展示逻辑,在前面的个人信息修改中其实用到的就是这种方式。

关于接口的设计以及前后端交互数据的设计大家可以参考一下第 10 课中的内容,分类模块在后台管理系统中有 5 个接口,分别是:

  • 分类列表分页接口
  • 添加分类接口
  • 根据 id 获取单条分类记录接口
  • 修改分类接口
  • 删除分类接口

接下来讲解每个接口具体的实现代码,首先在 controller/admin 包下新建 CategoryController.java,并在 service 包下新建业务层代码 CategoryService.java 及实现类,之后参照接口分别进行功能实现。

分类列表分页接口

列表接口负责接收前端传来的分页参数,如 pagelimit 等参数,之后将数据总数和对应页面的数据列表查询出来并封装为分页数据返回给前端。

  • 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CategoryController.java):

接口的映射地址为 /categories/list,请求方法为 GET,代码如下:

    /**      * 分类列表      */     @RequestMapping(value = "/categories/list", method = RequestMethod.GET)     @ResponseBody     public Result list(@RequestParam Map<String, Object> params) {         if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) {             return ResultGenerator.genFailResult("参数异常!");         }         PageQueryUtil pageUtil = new PageQueryUtil(params);         return ResultGenerator.genSuccessResult(categoryService.getBlogCategoryPage(pageUtil));     }
  • 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CategoryServiceImpl.java):
    public PageResult getBlogCategoryPage(PageQueryUtil pageUtil) {         List<BlogCategory> categoryList = blogCategoryMapper.findCategoryList(pageUtil);         int total = blogCategoryMapper.getTotalCategories(pageUtil);         PageResult pageResult = new PageResult(categoryList, total, pageUtil.getLimit(), pageUtil.getPage());         return pageResult;     }
  • BlogCategoryMapper.xml (注:完整代码位于 resources/mapper/BlogCategoryMapper.xml):
    <select id="findCategoryList" parameterType="Map" resultMap="BaseResultMap">         select         <include refid="Base_Column_List"/>         from tb_blog_category         where is_deleted=0         order by category_rank desc,create_time desc         <if test="start!=null and limit!=null">             limit #{start},#{limit}         </if>     </select>      <select id="getTotalCategories" parameterType="Map" resultType="int">     select count(*)  from tb_blog_category     where is_deleted=0     </select>

SQL 语句在 BlogCategoryMapper.xml 文件中,一般的分页也就是使用 limit 关键字实现,获取响应条数的记录和总数之后再进行数据封装,这个接口就是根据前端传的分页参数进行查询并返回分页数据以供前端页面进行数据渲染。

添加分类接口

添加接口负责接收前端的 POST 请求并处理其中的参数,接收的参数为 categoryName 字段和 categoryIcon 字段,categoryName 为分类名称,categoryIcon 字段为分类的图标字段。

  • 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CategoryController.java):

接口的映射地址为 /categories/save,请求方法为 POST,代码如下:

    /**      * 分类添加      */     @RequestMapping(value = "/categories/save", method = RequestMethod.POST)     @ResponseBody     public Result save(@RequestParam("categoryName") String categoryName,                        @RequestParam("categoryIcon") String categoryIcon) {         if (StringUtils.isEmpty(categoryName)) {             return ResultGenerator.genFailResult("请输入分类名称!");         }         if (StringUtils.isEmpty(categoryIcon)) {             return ResultGenerator.genFailResult("请选择分类图标!");         }         if (categoryService.saveCategory(categoryName, categoryIcon)) {             return ResultGenerator.genSuccessResult();         } else {             return ResultGenerator.genFailResult("分类名称重复");         }     }
  • 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CategoryServiceImpl.java):
    public Boolean saveCategory(String categoryName, String categoryIcon) {         BlogCategory temp = blogCategoryMapper.selectByCategoryName(categoryName);         if (temp == null) {             BlogCategory blogCategory = new BlogCategory();             blogCategory.setCategoryName(categoryName);             blogCategory.setCategoryIcon(categoryIcon);             return blogCategoryMapper.insertSelective(blogCategory) > 0;         }         return false;     }

SQL 语句在 BlogCategoryMapper.xml 文件中,是自动生成的 SQL,这里就不贴了。

添加接口中,首先会对参数进行校验,之后交给业务层代码进行操作,在 saveCategory() 方法中,首先会根据名称查询是否已经存在该分类,之后才会进行数据封装并进行数据库 insert 操作。

删除分类接口

删除接口负责接收前端的分类删除请求,处理前端传输过来的数据后,将这些记录从数据库中删除,这里的“删除”功能并不是真正意义上的删除,而是逻辑删除,我们将接受的参数设置为一个数组,可以同时删除多条记录,只需要在前端将用户选择的记录 id 封装好再传参到后端即可。

  • 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CategoryController.java):

接口的映射地址为 /categories/delete,请求方法为 POST,代码如下:

     /**      * 分类删除      */     @RequestMapping(value = "/categories/delete", method = RequestMethod.POST)     @ResponseBody     public Result delete(@RequestBody Integer[] ids) {         if (ids.length < 1) {             return ResultGenerator.genFailResult("参数异常!");         }         if (categoryService.deleteBatch(ids)) {             return ResultGenerator.genSuccessResult();         } else {             return ResultGenerator.genFailResult("删除失败");         }     }
  • 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CategoryServiceImpl.java):
    public Boolean deleteBatch(Integer[] ids) {         if (ids.length < 1) {             return false;         }         //删除分类数据         return blogCategoryMapper.deleteBatch(ids) > 0;     }
  • BlogCategoryMapper.xml (注:完整代码位于 resources/mapper/BlogCategoryMapper.xml):
    <update id="deleteBatch">         update tb_blog_category         set is_deleted=1 where category_id in         <foreach item="id" collection="array" open="(" separator="," close=")">             #{id}         </foreach>     </update>

接口的请求路径为 /categories/delete,并使用 @RequestBody 将前端传过来的参数封装为 id 数组,参数验证通过后则调用 deleteBatch() 批量删除方法进行数据库操作,否则将向前端返回错误信息。

其它

还有根据 id 获取详情的接口,路径为 categories/info/{id},请求方法为 GET;分类修改接口,路径为 categories/update,请求方法为 POST。

在接口设计和实现完成后,需要对接口进行测试,如果出现问题立刻修复,以上这些代码我都已经编写完成并将代码以压缩包的形式提供,大家在学习时可以直接使用我给的代码进行练习。

前端页面实现

category.html

在 resources/templates/admin 目录下新建 category.html,并引入对应的 js 文件和 css 样式文件。

功能按钮

分类管理模块我们也设计了常用的几个功能:分类信息增加、分类信息编辑、分类信息删除,因此在页面中添加对应的功能按钮以及触发事件,代码如下:

<div class="grid-btn">   <button class="btn btn-info" onclick="categoryAdd()">     <i class="fa fa-plus"></i>&nbsp;新增   </button>   <button class="btn btn-info" onclick="categoryEdit()">     <i class="fa fa-pencil-square-o"></i>&nbsp;修改   </button>   <button class="btn btn-danger" onclick="deleteCagegory()">     <i class="fa fa-trash-o"></i>&nbsp;删除   </button> </div>

分别是添加按钮,对应的触发事件是 categoryAdd() 方法,修改按钮,对应的触发事件是 categoryEdit() 方法,删除按钮,对应的触发事件是 deleteCagegory() 方法,这些方法我们将在后续的代码实现中给出。

分页信息展示区域

页面中已经引入 JqGrid 的相关静态资源文件,需要在页面中展示分页数据的区域增加如下代码:

<table id="jqGrid" class="table table-bordered"></table> <div id="jqGridPager"></div>

页面效果

那么最终的静态页面效果展示如下图所示:

19.博客系统项目开发之分类模块功能实现

图片描述

包括功能按钮、数据列表展示区域以及翻页功能区域,此时只是静态效果展示,并没有与后端进行数据交互,接下来我们将结合 Ajax 和后端接口实现具体的功能。

分类模块前端功能实现

完成了页面展示的实现,接着完成相关的功能实现。请大家一定自行对照源码完成代码编写。

分页功能

在 resources/static/admin/dist/js 目录下新增 category.js 文件,并添加如下代码:

Spring Boot 博客系统项目开发之分类模块功能实现

19.博客系统项目开发之分类模块功能实现

本项目的开源仓库地址为:https://github.com/ZHENFENG13/My-Blog

除 My-Blog 项目外,我也在维护另外一个开源项目,仓库地址为:https://github.com/newbee-ltd/newbee-mall

19.博客系统项目开发之分类模块功能实现

感兴趣的朋友可以去关注一下。

文章总览

前文已经实现了用户的登录及权限验证模块,接下来将会继续完善后台功能,开发博客系统的文章相关模块,本节实验将会对博客的分类模块进行介绍和功能开发及完善。

知识点

  • 分类模块介绍
  • 分类模块表结构设计及接口实现
  • 分类模块页面设计及编码
  • 分类功能测试

环境

  • JDK 1.8 或者更高版本
  • Spring Boot 2.1.0-RELEASE
  • Maven 3+

分类模块简介

在博客系统中,分类模块的设计是不可缺少的,我们在各大博客网站中都能够看到这个模块设计,在浏览文章的过程中,我们也会挑选出我们感兴趣类别中的文章进行阅读,比如看了一篇架构的文章,觉得不错,那么就想接着去看这个类别下的其他文章,或者你偏爱前端类别下的内容,那就可以针对性的浏览所有前端类别下的文章,因此对博文进行归类是十分必要的。

分类是通过比较事物之间的相似性,把具有某些共同点或相似特征的事物归属于一个不确定集合的逻辑方法,而对应的,我们可以说分类别的作用是使一个大集合中的内容条理清楚,层次分明,接下来是功能开发的讲解。

表结构设计及 Mapper 文件自动生成

表结构设计

在进行接口设计和具体的功能实现前,首先将表结构确定下来,每篇文章都会被归类到一个类别下,一个类别下会有多篇文章,分类实体与文章实体的关系是一对多的关系,因此在表结构设计时,在文章表中设置一个分类关联字段即可,分类表只需要将分类相关的字段定义好,分类实体与文章实体的关系交给文章表来维护即可(后续讲到文章表时再介绍),分类表的 SQL 设计如下,直接执行如下 SQL 语句即可:

USE `my_blog_db`;  /*Table structure for table `tb_blog_category` */  CREATE TABLE `tb_blog_category` (   `category_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '分类表主键',   `category_name` varchar(50) NOT NULL COMMENT '分类的名称',   `category_icon` varchar(50) NOT NULL COMMENT '分类的图标',   `category_rank` int(11) NOT NULL DEFAULT '1' COMMENT '分类的排序值 被使用的越多数值越大',   `is_deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 0=否 1=是',   `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',   PRIMARY KEY (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

分类表的字段以及每个字段对应的含义都在上面的 SQL 中有介绍,大家可以对照 SQL 进行理解,把表结构导入到数据库中即可,接下来我们进行编码工作。

MyBatis-Generator 插件自动生成 Mapper 文件

首先,我们使用 MyBatis-Generator 插件将该表对应的 Mapper 文件及对应的实体类和 Dao 层接口生成出来,这部分代码就不贴在文章中了,大家可以通过下载源码来查看。

需要注意的是,在代码生成后需要在 dao 层下的 BlogCategoryMapper 接口类上添加 @Mapper 注解以将其注册到 IOC 容器中以供后续调用(如果已经在主类上添加 @MapperScan 注解这一步可以省略)。其次,在 tb_blog_category 表中,我们设计了一个 is_deleted 字段,用于逻辑删除的标志位,由于 is_deleted 的字段设计,我们对表中数据的删除都是软删除,而不是真正意义的删除,只是做了一个删除标志位,如果此字段为 1 则表示已经被删除不再使用,因为是个人博客,这么做的目的主要也是为了防止误删,因此我们需要修改 Mapper 文件中的 查询语句和删除语句,将 is_deleted 条件带上,修改后的语句如下:(注:完整代码位于 resources/mapper/BlogCategoryMapper.xml)

   <select id="findCategoryList" parameterType="Map" resultMap="BaseResultMap">         select         <include refid="Base_Column_List"/>         from tb_blog_category         where is_deleted=0         order by category_rank desc,create_time desc         <if test="start!=null and limit!=null">             limit #{start},#{limit}         </if>     </select>      <select id="getTotalCategories" parameterType="Map" resultType="int">     select count(*)  from tb_blog_category     where is_deleted=0     </select>      <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">         select         <include refid="Base_Column_List"/>         from tb_blog_category         where category_id = #{categoryId,jdbcType=INTEGER} AND is_deleted = 0     </select>      <update id="deleteByPrimaryKey" parameterType="java.lang.Integer">     UPDATE tb_blog_category SET  is_deleted = 1     where category_id = #{categoryId,jdbcType=VARCHAR} AND is_deleted = 0     </update>

通过以上代码我们可以看出,在删除操作时我们并不是执行 delete 语句,而是将需要删除的这条记录的 is_deleted 字段修改为 1,这样就表示该行记录已经被执行了删除操作,那么其他的 select 查询语句就需要在查询条件中添加 is_deleted = 0 将“被删除”的记录给过滤出去,这个知识点希望大家能够理解,在后续的其他功能模块中我们也会使用软删除的设计。

分类模块接口设计及实现

接口介绍

为了让页面体验更加友好,就不采用传统的 MVC 跳转模式,一个功能一个页面,这种交互感觉有些浪费,翻页的时候,翻一页跳转一次也比较繁琐,添加或者新增的时候也要进行页面跳转,所以这些功能的实现就采用通过 Ajax 异步与后端交互数据,当使用者点击了页面上的元素,此时触发响应的 js 事件,进而通过 Ajax 的方式向后端请求数据,前端再根据后端返回的数据内容去进行响应的展示逻辑,在前面的个人信息修改中其实用到的就是这种方式。

关于接口的设计以及前后端交互数据的设计大家可以参考一下第 10 课中的内容,分类模块在后台管理系统中有 5 个接口,分别是:

  • 分类列表分页接口
  • 添加分类接口
  • 根据 id 获取单条分类记录接口
  • 修改分类接口
  • 删除分类接口

接下来讲解每个接口具体的实现代码,首先在 controller/admin 包下新建 CategoryController.java,并在 service 包下新建业务层代码 CategoryService.java 及实现类,之后参照接口分别进行功能实现。

分类列表分页接口

列表接口负责接收前端传来的分页参数,如 pagelimit 等参数,之后将数据总数和对应页面的数据列表查询出来并封装为分页数据返回给前端。

  • 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CategoryController.java):

接口的映射地址为 /categories/list,请求方法为 GET,代码如下:

    /**      * 分类列表      */     @RequestMapping(value = "/categories/list", method = RequestMethod.GET)     @ResponseBody     public Result list(@RequestParam Map<String, Object> params) {         if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) {             return ResultGenerator.genFailResult("参数异常!");         }         PageQueryUtil pageUtil = new PageQueryUtil(params);         return ResultGenerator.genSuccessResult(categoryService.getBlogCategoryPage(pageUtil));     }
  • 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CategoryServiceImpl.java):
    public PageResult getBlogCategoryPage(PageQueryUtil pageUtil) {         List<BlogCategory> categoryList = blogCategoryMapper.findCategoryList(pageUtil);         int total = blogCategoryMapper.getTotalCategories(pageUtil);         PageResult pageResult = new PageResult(categoryList, total, pageUtil.getLimit(), pageUtil.getPage());         return pageResult;     }
  • BlogCategoryMapper.xml (注:完整代码位于 resources/mapper/BlogCategoryMapper.xml):
    <select id="findCategoryList" parameterType="Map" resultMap="BaseResultMap">         select         <include refid="Base_Column_List"/>         from tb_blog_category         where is_deleted=0         order by category_rank desc,create_time desc         <if test="start!=null and limit!=null">             limit #{start},#{limit}         </if>     </select>      <select id="getTotalCategories" parameterType="Map" resultType="int">     select count(*)  from tb_blog_category     where is_deleted=0     </select>

SQL 语句在 BlogCategoryMapper.xml 文件中,一般的分页也就是使用 limit 关键字实现,获取响应条数的记录和总数之后再进行数据封装,这个接口就是根据前端传的分页参数进行查询并返回分页数据以供前端页面进行数据渲染。

添加分类接口

添加接口负责接收前端的 POST 请求并处理其中的参数,接收的参数为 categoryName 字段和 categoryIcon 字段,categoryName 为分类名称,categoryIcon 字段为分类的图标字段。

  • 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CategoryController.java):

接口的映射地址为 /categories/save,请求方法为 POST,代码如下:

    /**      * 分类添加      */     @RequestMapping(value = "/categories/save", method = RequestMethod.POST)     @ResponseBody     public Result save(@RequestParam("categoryName") String categoryName,                        @RequestParam("categoryIcon") String categoryIcon) {         if (StringUtils.isEmpty(categoryName)) {             return ResultGenerator.genFailResult("请输入分类名称!");         }         if (StringUtils.isEmpty(categoryIcon)) {             return ResultGenerator.genFailResult("请选择分类图标!");         }         if (categoryService.saveCategory(categoryName, categoryIcon)) {             return ResultGenerator.genSuccessResult();         } else {             return ResultGenerator.genFailResult("分类名称重复");         }     }
  • 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CategoryServiceImpl.java):
    public Boolean saveCategory(String categoryName, String categoryIcon) {         BlogCategory temp = blogCategoryMapper.selectByCategoryName(categoryName);         if (temp == null) {             BlogCategory blogCategory = new BlogCategory();             blogCategory.setCategoryName(categoryName);             blogCategory.setCategoryIcon(categoryIcon);             return blogCategoryMapper.insertSelective(blogCategory) > 0;         }         return false;     }

SQL 语句在 BlogCategoryMapper.xml 文件中,是自动生成的 SQL,这里就不贴了。

添加接口中,首先会对参数进行校验,之后交给业务层代码进行操作,在 saveCategory() 方法中,首先会根据名称查询是否已经存在该分类,之后才会进行数据封装并进行数据库 insert 操作。

删除分类接口

删除接口负责接收前端的分类删除请求,处理前端传输过来的数据后,将这些记录从数据库中删除,这里的“删除”功能并不是真正意义上的删除,而是逻辑删除,我们将接受的参数设置为一个数组,可以同时删除多条记录,只需要在前端将用户选择的记录 id 封装好再传参到后端即可。

  • 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CategoryController.java):

接口的映射地址为 /categories/delete,请求方法为 POST,代码如下:

     /**      * 分类删除      */     @RequestMapping(value = "/categories/delete", method = RequestMethod.POST)     @ResponseBody     public Result delete(@RequestBody Integer[] ids) {         if (ids.length < 1) {             return ResultGenerator.genFailResult("参数异常!");         }         if (categoryService.deleteBatch(ids)) {             return ResultGenerator.genSuccessResult();         } else {             return ResultGenerator.genFailResult("删除失败");         }     }
  • 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CategoryServiceImpl.java):
    public Boolean deleteBatch(Integer[] ids) {         if (ids.length < 1) {             return false;         }         //删除分类数据         return blogCategoryMapper.deleteBatch(ids) > 0;     }
  • BlogCategoryMapper.xml (注:完整代码位于 resources/mapper/BlogCategoryMapper.xml):
    <update id="deleteBatch">         update tb_blog_category         set is_deleted=1 where category_id in         <foreach item="id" collection="array" open="(" separator="," close=")">             #{id}         </foreach>     </update>

接口的请求路径为 /categories/delete,并使用 @RequestBody 将前端传过来的参数封装为 id 数组,参数验证通过后则调用 deleteBatch() 批量删除方法进行数据库操作,否则将向前端返回错误信息。

其它

还有根据 id 获取详情的接口,路径为 categories/info/{id},请求方法为 GET;分类修改接口,路径为 categories/update,请求方法为 POST。

在接口设计和实现完成后,需要对接口进行测试,如果出现问题立刻修复,以上这些代码我都已经编写完成并将代码以压缩包的形式提供,大家在学习时可以直接使用我给的代码进行练习。

前端页面实现

category.html

在 resources/templates/admin 目录下新建 category.html,并引入对应的 js 文件和 css 样式文件。

功能按钮

分类管理模块我们也设计了常用的几个功能:分类信息增加、分类信息编辑、分类信息删除,因此在页面中添加对应的功能按钮以及触发事件,代码如下:

<div class="grid-btn">   <button class="btn btn-info" onclick="categoryAdd()">     <i class="fa fa-plus"></i>&nbsp;新增   </button>   <button class="btn btn-info" onclick="categoryEdit()">     <i class="fa fa-pencil-square-o"></i>&nbsp;修改   </button>   <button class="btn btn-danger" onclick="deleteCagegory()">     <i class="fa fa-trash-o"></i>&nbsp;删除   </button> </div>

分别是添加按钮,对应的触发事件是 categoryAdd() 方法,修改按钮,对应的触发事件是 categoryEdit() 方法,删除按钮,对应的触发事件是 deleteCagegory() 方法,这些方法我们将在后续的代码实现中给出。

分页信息展示区域

页面中已经引入 JqGrid 的相关静态资源文件,需要在页面中展示分页数据的区域增加如下代码:

<table id="jqGrid" class="table table-bordered"></table> <div id="jqGridPager"></div>

页面效果

那么最终的静态页面效果展示如下图所示:

19.博客系统项目开发之分类模块功能实现

图片描述

包括功能按钮、数据列表展示区域以及翻页功能区域,此时只是静态效果展示,并没有与后端进行数据交互,接下来我们将结合 Ajax 和后端接口实现具体的功能。

分类模块前端功能实现

完成了页面展示的实现,接着完成相关的功能实现。请大家一定自行对照源码完成代码编写。

分页功能

在 resources/static/admin/dist/js 目录下新增 category.js 文件,并添加如下代码:

Spring Boot 博客系统项目开发之分类模块功能实现

19.博客系统项目开发之分类模块功能实现

本项目的开源仓库地址为:https://github.com/ZHENFENG13/My-Blog

除 My-Blog 项目外,我也在维护另外一个开源项目,仓库地址为:https://github.com/newbee-ltd/newbee-mall

19.博客系统项目开发之分类模块功能实现

感兴趣的朋友可以去关注一下。

文章总览

前文已经实现了用户的登录及权限验证模块,接下来将会继续完善后台功能,开发博客系统的文章相关模块,本节实验将会对博客的分类模块进行介绍和功能开发及完善。

知识点

  • 分类模块介绍
  • 分类模块表结构设计及接口实现
  • 分类模块页面设计及编码
  • 分类功能测试

环境

  • JDK 1.8 或者更高版本
  • Spring Boot 2.1.0-RELEASE
  • Maven 3+

分类模块简介

在博客系统中,分类模块的设计是不可缺少的,我们在各大博客网站中都能够看到这个模块设计,在浏览文章的过程中,我们也会挑选出我们感兴趣类别中的文章进行阅读,比如看了一篇架构的文章,觉得不错,那么就想接着去看这个类别下的其他文章,或者你偏爱前端类别下的内容,那就可以针对性的浏览所有前端类别下的文章,因此对博文进行归类是十分必要的。

分类是通过比较事物之间的相似性,把具有某些共同点或相似特征的事物归属于一个不确定集合的逻辑方法,而对应的,我们可以说分类别的作用是使一个大集合中的内容条理清楚,层次分明,接下来是功能开发的讲解。

表结构设计及 Mapper 文件自动生成

表结构设计

在进行接口设计和具体的功能实现前,首先将表结构确定下来,每篇文章都会被归类到一个类别下,一个类别下会有多篇文章,分类实体与文章实体的关系是一对多的关系,因此在表结构设计时,在文章表中设置一个分类关联字段即可,分类表只需要将分类相关的字段定义好,分类实体与文章实体的关系交给文章表来维护即可(后续讲到文章表时再介绍),分类表的 SQL 设计如下,直接执行如下 SQL 语句即可:

USE `my_blog_db`;  /*Table structure for table `tb_blog_category` */  CREATE TABLE `tb_blog_category` (   `category_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '分类表主键',   `category_name` varchar(50) NOT NULL COMMENT '分类的名称',   `category_icon` varchar(50) NOT NULL COMMENT '分类的图标',   `category_rank` int(11) NOT NULL DEFAULT '1' COMMENT '分类的排序值 被使用的越多数值越大',   `is_deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 0=否 1=是',   `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',   PRIMARY KEY (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

分类表的字段以及每个字段对应的含义都在上面的 SQL 中有介绍,大家可以对照 SQL 进行理解,把表结构导入到数据库中即可,接下来我们进行编码工作。

MyBatis-Generator 插件自动生成 Mapper 文件

首先,我们使用 MyBatis-Generator 插件将该表对应的 Mapper 文件及对应的实体类和 Dao 层接口生成出来,这部分代码就不贴在文章中了,大家可以通过下载源码来查看。

需要注意的是,在代码生成后需要在 dao 层下的 BlogCategoryMapper 接口类上添加 @Mapper 注解以将其注册到 IOC 容器中以供后续调用(如果已经在主类上添加 @MapperScan 注解这一步可以省略)。其次,在 tb_blog_category 表中,我们设计了一个 is_deleted 字段,用于逻辑删除的标志位,由于 is_deleted 的字段设计,我们对表中数据的删除都是软删除,而不是真正意义的删除,只是做了一个删除标志位,如果此字段为 1 则表示已经被删除不再使用,因为是个人博客,这么做的目的主要也是为了防止误删,因此我们需要修改 Mapper 文件中的 查询语句和删除语句,将 is_deleted 条件带上,修改后的语句如下:(注:完整代码位于 resources/mapper/BlogCategoryMapper.xml)

   <select id="findCategoryList" parameterType="Map" resultMap="BaseResultMap">         select         <include refid="Base_Column_List"/>         from tb_blog_category         where is_deleted=0         order by category_rank desc,create_time desc         <if test="start!=null and limit!=null">             limit #{start},#{limit}         </if>     </select>      <select id="getTotalCategories" parameterType="Map" resultType="int">     select count(*)  from tb_blog_category     where is_deleted=0     </select>      <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">         select         <include refid="Base_Column_List"/>         from tb_blog_category         where category_id = #{categoryId,jdbcType=INTEGER} AND is_deleted = 0     </select>      <update id="deleteByPrimaryKey" parameterType="java.lang.Integer">     UPDATE tb_blog_category SET  is_deleted = 1     where category_id = #{categoryId,jdbcType=VARCHAR} AND is_deleted = 0     </update>

通过以上代码我们可以看出,在删除操作时我们并不是执行 delete 语句,而是将需要删除的这条记录的 is_deleted 字段修改为 1,这样就表示该行记录已经被执行了删除操作,那么其他的 select 查询语句就需要在查询条件中添加 is_deleted = 0 将“被删除”的记录给过滤出去,这个知识点希望大家能够理解,在后续的其他功能模块中我们也会使用软删除的设计。

分类模块接口设计及实现

接口介绍

为了让页面体验更加友好,就不采用传统的 MVC 跳转模式,一个功能一个页面,这种交互感觉有些浪费,翻页的时候,翻一页跳转一次也比较繁琐,添加或者新增的时候也要进行页面跳转,所以这些功能的实现就采用通过 Ajax 异步与后端交互数据,当使用者点击了页面上的元素,此时触发响应的 js 事件,进而通过 Ajax 的方式向后端请求数据,前端再根据后端返回的数据内容去进行响应的展示逻辑,在前面的个人信息修改中其实用到的就是这种方式。

关于接口的设计以及前后端交互数据的设计大家可以参考一下第 10 课中的内容,分类模块在后台管理系统中有 5 个接口,分别是:

  • 分类列表分页接口
  • 添加分类接口
  • 根据 id 获取单条分类记录接口
  • 修改分类接口
  • 删除分类接口

接下来讲解每个接口具体的实现代码,首先在 controller/admin 包下新建 CategoryController.java,并在 service 包下新建业务层代码 CategoryService.java 及实现类,之后参照接口分别进行功能实现。

分类列表分页接口

列表接口负责接收前端传来的分页参数,如 pagelimit 等参数,之后将数据总数和对应页面的数据列表查询出来并封装为分页数据返回给前端。

  • 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CategoryController.java):

接口的映射地址为 /categories/list,请求方法为 GET,代码如下:

    /**      * 分类列表      */     @RequestMapping(value = "/categories/list", method = RequestMethod.GET)     @ResponseBody     public Result list(@RequestParam Map<String, Object> params) {         if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) {             return ResultGenerator.genFailResult("参数异常!");         }         PageQueryUtil pageUtil = new PageQueryUtil(params);         return ResultGenerator.genSuccessResult(categoryService.getBlogCategoryPage(pageUtil));     }
  • 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CategoryServiceImpl.java):
    public PageResult getBlogCategoryPage(PageQueryUtil pageUtil) {         List<BlogCategory> categoryList = blogCategoryMapper.findCategoryList(pageUtil);         int total = blogCategoryMapper.getTotalCategories(pageUtil);         PageResult pageResult = new PageResult(categoryList, total, pageUtil.getLimit(), pageUtil.getPage());         return pageResult;     }
  • BlogCategoryMapper.xml (注:完整代码位于 resources/mapper/BlogCategoryMapper.xml):
    <select id="findCategoryList" parameterType="Map" resultMap="BaseResultMap">         select         <include refid="Base_Column_List"/>         from tb_blog_category         where is_deleted=0         order by category_rank desc,create_time desc         <if test="start!=null and limit!=null">             limit #{start},#{limit}         </if>     </select>      <select id="getTotalCategories" parameterType="Map" resultType="int">     select count(*)  from tb_blog_category     where is_deleted=0     </select>

SQL 语句在 BlogCategoryMapper.xml 文件中,一般的分页也就是使用 limit 关键字实现,获取响应条数的记录和总数之后再进行数据封装,这个接口就是根据前端传的分页参数进行查询并返回分页数据以供前端页面进行数据渲染。

添加分类接口

添加接口负责接收前端的 POST 请求并处理其中的参数,接收的参数为 categoryName 字段和 categoryIcon 字段,categoryName 为分类名称,categoryIcon 字段为分类的图标字段。

  • 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CategoryController.java):

接口的映射地址为 /categories/save,请求方法为 POST,代码如下:

    /**      * 分类添加      */     @RequestMapping(value = "/categories/save", method = RequestMethod.POST)     @ResponseBody     public Result save(@RequestParam("categoryName") String categoryName,                        @RequestParam("categoryIcon") String categoryIcon) {         if (StringUtils.isEmpty(categoryName)) {             return ResultGenerator.genFailResult("请输入分类名称!");         }         if (StringUtils.isEmpty(categoryIcon)) {             return ResultGenerator.genFailResult("请选择分类图标!");         }         if (categoryService.saveCategory(categoryName, categoryIcon)) {             return ResultGenerator.genSuccessResult();         } else {             return ResultGenerator.genFailResult("分类名称重复");         }     }
  • 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CategoryServiceImpl.java):
    public Boolean saveCategory(String categoryName, String categoryIcon) {         BlogCategory temp = blogCategoryMapper.selectByCategoryName(categoryName);         if (temp == null) {             BlogCategory blogCategory = new BlogCategory();             blogCategory.setCategoryName(categoryName);             blogCategory.setCategoryIcon(categoryIcon);             return blogCategoryMapper.insertSelective(blogCategory) > 0;         }         return false;     }

SQL 语句在 BlogCategoryMapper.xml 文件中,是自动生成的 SQL,这里就不贴了。

添加接口中,首先会对参数进行校验,之后交给业务层代码进行操作,在 saveCategory() 方法中,首先会根据名称查询是否已经存在该分类,之后才会进行数据封装并进行数据库 insert 操作。

删除分类接口

删除接口负责接收前端的分类删除请求,处理前端传输过来的数据后,将这些记录从数据库中删除,这里的“删除”功能并不是真正意义上的删除,而是逻辑删除,我们将接受的参数设置为一个数组,可以同时删除多条记录,只需要在前端将用户选择的记录 id 封装好再传参到后端即可。

  • 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CategoryController.java):

接口的映射地址为 /categories/delete,请求方法为 POST,代码如下:

     /**      * 分类删除      */     @RequestMapping(value = "/categories/delete", method = RequestMethod.POST)     @ResponseBody     public Result delete(@RequestBody Integer[] ids) {         if (ids.length < 1) {             return ResultGenerator.genFailResult("参数异常!");         }         if (categoryService.deleteBatch(ids)) {             return ResultGenerator.genSuccessResult();         } else {             return ResultGenerator.genFailResult("删除失败");         }     }
  • 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CategoryServiceImpl.java):
    public Boolean deleteBatch(Integer[] ids) {         if (ids.length < 1) {             return false;         }         //删除分类数据         return blogCategoryMapper.deleteBatch(ids) > 0;     }
  • BlogCategoryMapper.xml (注:完整代码位于 resources/mapper/BlogCategoryMapper.xml):
    <update id="deleteBatch">         update tb_blog_category         set is_deleted=1 where category_id in         <foreach item="id" collection="array" open="(" separator="," close=")">             #{id}         </foreach>     </update>

接口的请求路径为 /categories/delete,并使用 @RequestBody 将前端传过来的参数封装为 id 数组,参数验证通过后则调用 deleteBatch() 批量删除方法进行数据库操作,否则将向前端返回错误信息。

其它

还有根据 id 获取详情的接口,路径为 categories/info/{id},请求方法为 GET;分类修改接口,路径为 categories/update,请求方法为 POST。

在接口设计和实现完成后,需要对接口进行测试,如果出现问题立刻修复,以上这些代码我都已经编写完成并将代码以压缩包的形式提供,大家在学习时可以直接使用我给的代码进行练习。

前端页面实现

category.html

在 resources/templates/admin 目录下新建 category.html,并引入对应的 js 文件和 css 样式文件。

功能按钮

分类管理模块我们也设计了常用的几个功能:分类信息增加、分类信息编辑、分类信息删除,因此在页面中添加对应的功能按钮以及触发事件,代码如下:

<div class="grid-btn">   <button class="btn btn-info" onclick="categoryAdd()">     <i class="fa fa-plus"></i>&nbsp;新增   </button>   <button class="btn btn-info" onclick="categoryEdit()">     <i class="fa fa-pencil-square-o"></i>&nbsp;修改   </button>   <button class="btn btn-danger" onclick="deleteCagegory()">     <i class="fa fa-trash-o"></i>&nbsp;删除   </button> </div>

分别是添加按钮,对应的触发事件是 categoryAdd() 方法,修改按钮,对应的触发事件是 categoryEdit() 方法,删除按钮,对应的触发事件是 deleteCagegory() 方法,这些方法我们将在后续的代码实现中给出。

分页信息展示区域

页面中已经引入 JqGrid 的相关静态资源文件,需要在页面中展示分页数据的区域增加如下代码:

<table id="jqGrid" class="table table-bordered"></table> <div id="jqGridPager"></div>

页面效果

那么最终的静态页面效果展示如下图所示:

19.博客系统项目开发之分类模块功能实现

图片描述

包括功能按钮、数据列表展示区域以及翻页功能区域,此时只是静态效果展示,并没有与后端进行数据交互,接下来我们将结合 Ajax 和后端接口实现具体的功能。

分类模块前端功能实现

完成了页面展示的实现,接着完成相关的功能实现。请大家一定自行对照源码完成代码编写。

分页功能

在 resources/static/admin/dist/js 目录下新增 category.js 文件,并添加如下代码:

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 19.博客系统项目开发之分类模块功能实现求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们