SpringBoot聚合项目:达内知道(五)-开发学生首页问题列表、PageHelper、补充:业务开发顺序

1.开发学生首页问题列表

问题列表区域:

问题列表显示流程:

  1.页面加载完毕之后运行

  2.查询当前登录用户的所有问题列表

  3.从Spring-Security中获得当前登录用户信息

  4.业务逻辑层查询当前用户的所有问题

  5.返回显示

1.1 查询当前用户问题列表的业务逻辑层

  编写业务逻辑层先写接口,在IQuestionService接口中添加方法:

 package cn.tedu.knows.portal.service;
 
 import cn.tedu.knows.portal.model.Question;
 import com.baomidou.mybatisplus.extension.service.IService;
 
 import java.util.List;
 
 /**
  * <p>
  * 服务类
  * </p>
  *
  * @author tedu.cn
  * @since 2021-08-23
  */
 public interface IQuestionService extends IService<Question> {
     //根据当前登录用户查询所有问题列表
     List<Question> getMyQuestions(String username);
 
 }
 

在接口上Ctrl+Alt+B跳转到QuestionServiceImpl实现类,编写代码如下:

 //根据用户名查询用户用
 @Autowired
 private UserMapper userMapper;
 
 //查询问题用
 @Autowired
 private QuestionMapper questionMapper;
 
 //重写方法
 @Override
 public List<Question> getMyQuestions(String username) {
     //1.根据用户名查询用户信息
     User user=userMapper.findUserByUsername(username);
     //2.根据用户信息进行查询操作(QueryWrapper查询)
     QueryWrapper<Question> query=new QueryWrapper<>();
     query.eq("user_id",user.getId());//根据用户id查
     query.eq("delete_status",0);//并且不能是删除的问题
     //3.执行查询
     List<Question> questions=questionMapper.selectList(query);
     //4.返回查询结果
     // 千万别忘了返回!!!
     return questions;
 }

1.2 编写查询用户问题列表的控制层

  业务逻辑层编写完毕,需要在控制层中调用,而且控制层中要获得当前登录用户的信息。

QuestionController编写代码如下:

 package cn.tedu.knows.portal.controller;
 
 
 import cn.tedu.knows.portal.model.Question;
 import cn.tedu.knows.portal.service.IQuestionService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
 
 /**
  * <p>
  * 前端控制器
  * </p>
  *
  * @author tedu.cn
  * @since 2021-08-23
  */
 @RestController
 //@RequestMapping("/portal/question")
 @RequestMapping("/v1/questions") //微服务框架标准
 public class QuestionController {
     //查询当前用户问题列表
     @Autowired
     private IQuestionService questionService;
     
     //根据当前登录用户,查询用户问题列表的控制层方法
     @GetMapping("/my") //localhost:8080/v1/questions/my
     //参数中可以使用一个注解获得当前登录用户的信息(从Spring Security中获取)
     public List<Question> my(@AuthenticationPrincipal UserDetails user){
         List<Question> questions = questionService.getMyQuestions(user.getUsername());
         //返回查询到的所有问题
         return questions;
    }
 }
 

重启服务,输入同步路径:localhost:8080/v1/questions/my,进行测试,输出结果:

1.3 Vue绑定和页面引用

在index_student.html的188行附近:

 <div class="media bg-white m-2 p-3"
      v-for="question in questions" ><!--遍历问题列表-->
     <!-- ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ -->
   <div class="media-body w-50">
     <div class="row">
       <div class="col-md-12 col-lg-2">
         <span class="badge badge-pill badge-warning" style="display: none">未回复</span>
         <span class="badge badge-pill badge-info" style="display: none">已回复</span>
         <span class="badge badge-pill badge-success">已解决</span>
       </div>
       <div class="col-md-12 col-lg-10">
         <h5 class="mt-0 mb-1 text-truncate">
           <a class="text-dark" href="question/detail.html"
             v-text="question.title"><!--页面绑定-->
       <!-- ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ -->
            eclipse 如何导入项目?
           </a>
         </h5>
       </div>
     </div>
     <div class="font-weight-light text-truncate text-wrap text-justify mb-2" style="height: 70px;">
       <p v-html="question.content"><!--页面绑定:注意1此处是v-html,它不仅能显示内容,还能解析HTML内容,实现解析效果-->
      <!-- ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ -->
        eclipse 如何导入项目?
       </p>
     </div>
     <div class="row">
       <div class="col-12 mt-1 text-info">
         <i class="fa fa-tags" aria-hidden="true"></i>
         <a class="text-info badge badge-pill bg-light" href="tag/tag_question.html"><small >Java基础 &nbsp;</small></a>
       </div>
     </div>
     <div class="row">
       <div class="col-12 text-right">
         <div class="list-inline mb-1 ">
           <small class="list-inline-item"
             v-text="question.userNickName">风继续吹</small><!--页面绑定-->
             <!-- ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ -->
           <small class="list-inline-item">
             <span v-text="question.pageViews">12</span>浏览<!--页面绑定-->
             <!-- ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ -->
             </small>
           <small class="list-inline-item" >13分钟前</small>
         </div>
       </div>
     </div>
 
   </div>
   <!-- / class="media-body"-->
   <img src="img/tags/example0.jpg"  class="ml-3 border img-fluid rounded" alt="" width="208" height="116">
 </div>

页面尾部添加引用:

 </body>
 <script src="js/utils.js"></script>
 <script src="js/tags_nav.js"></script>
 <!-- ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ -->
 <script src="js/index.js"></script>
 <!-- ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ -->
 </html>

重启服务后测试效果:

1.4 显示持续时间

  数据库中保存的时间是年月日时分秒的格式,但是现在网站中流行显示时间差,所以我们需要通过设计和计算实现显示持续时间。我们问题列表的持续时间设计为4段:

  1. <1分钟:显示”刚刚“

  2. <1小时:显示“n分钟前“

  3. <24小时:显示“n小时前“

  4. >24小时:显示“n天前“

  计算思路就是:当前时间-问题发布时间,判断这个时间在哪个段中,根据判断结果计算显示时间。

在index_student.html页面修改显示持续时间:224行附近

 <div class="list-inline mb-1 ">
   <small class="list-inline-item"
     v-text="question.userNickName">风继续吹</small>
   <small class="list-inline-item">
     <span v-text="question.pageViews">12</span>浏览</small>
   <small class="list-inline-item"
     v-text="question.duration">13分钟前</small><!--修改显示时间-->
     <!-- ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ -->
 </div>

  从显示的时间发现,问题的显示顺序默认是时间升序的,分析实际需求,显示的条件应该是时间降序,那么我们在QuestionServiceImpl类中的getMyQuestions方法添加排序条件:

 //2.根据用户信息进行查询操作(QueryWrapper查询)
 QueryWrapper<Question> query = new QueryWrapper<>();
 query.eq("user_id",user.getId());//根据用户id查
 query.eq("delete_status",0);//并且不能是删除的
 // 新增代码 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
 //设置时间降序排序
 query.orderByDesc("createtime");
 //3.执行查询
 List<Question> questions=questionMapper.selectList(query);

  重启服务,修改问题创建时间,发现问题按时间降序排列。

1.5 显示标签列表

问题和标签的关系示意图:

  在Question表中保存了当前问题的所有标签名称,这种做法叫冗余存储,目的是避免复杂的连表查询,提高查询效率。当我们需要根据这个列保存的标签名称获得标签对象时,我们就希望有一个Map类型的对象,保存着所有的标签,方便我们使用标签名称来获得标签对象。

在Tag业务逻辑层创建一个Map类型包含所有标签对象的缓存集合,在ITagService添加方法:

 // 查询返回包含所有标签的Map
 Map<String,Tag> getTagMap();//key:标签名 value:标签对象

在接口上Ctrl+Alt+B跳到对应实现类TagServiceImpl中,实现代码如下:

 package cn.tedu.knows.portal.service.impl;
 
 import cn.tedu.knows.portal.model.Tag;
 import cn.tedu.knows.portal.mapper.TagMapper;
 import cn.tedu.knows.portal.service.ITagService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * <p>
  * 服务实现类
  * </p>
  *
  * @author tedu.cn
  * @since 2021-08-23
  */
 @Service
 public class TagServiceImpl extends ServiceImpl<TagMapper, Tag> implements ITagService {
     //声明一个List保存所有标签,用作缓存
     private List<Tag> tags = new CopyOnWriteArrayList<>();//线程安全的集合
 
     //声明一个Map保存所有标签,用于根据标签名获得标签对象
     private Map<String,Tag> tagMap = new ConcurrentHashMap<>();//线程安全的集合
 
     //从Spring容器中取TagMapper
     @Autowired
     private TagMapper tagMapper;
 
     //全查所有Tags
     @Override //重写方法
     public List<Tag> getTags() {
         //判断tags属性是否为空,如果为空,表示当前查询是第一次查询
         if(tags.isEmpty()){//保证效率
             synchronized (tags){ //考虑线程安全,加锁
                 if(tags.isEmpty()){//保证只有一次
                     List<Tag> list = tagMapper.selectList(null);//查询全部
                     tags.addAll(list);//把所有标签都放到线程安全的集合中
                     for (Tag t:list){
                         tagMap.put(t.getName(),t);//key:t.getName()标签名, value:t标签对象
                    }
                }
            }
        }
         return tags;
    }
 
     //全查所有Tags放在Map中
     @Override
     public Map<String, Tag> getTagMap() {
         //如果tagMap空,则list也为空,因为是先有的list,然后才有的tagMap
         if(tagMap.isEmpty()){//保证效率
             getTags();//调用上面的方法赋值
        }
         return tagMap;
    }
 }
 

  现在我们准备好了使用标签名称获得标签对象,但是我们获得的对象要保存到哪里呢?答案是要保存到当前的Question实体类中,在Question类代码添加属性:

 /**
 * 当前问题包含的标签集合
 * 这个属性没有对应的列,需要使用下面的注解表示,否则数据库会报错
 * @TableField(exist = false)
 */
 @TableField(exist = false)
 private List<Tag> tags;

  相当于添加了下面的tags属性:存储标签,由于每个问题的标签不止一个,并且是以字符串形式进行保存的,因此要首先对字符串进行拆分,得到字符串数组,然后根据每个标签名,获得对应的标签对象(问题列表的每个标签名都是带超链接的,点击后显示该标签所对应的问题)。

TagServiceImpl类中两个成员变量引用相同的对象,不会占用额外内存:

  字符串:"Java基础,Java SE,面试题"

  拆分:split(",")

  得到字符串数组:{"Java基础","Java SE","面试题"}

  遍历数组通过元素名称获得标签对象:<java基础>对象、<Java SE>对象、<面试题>对象

所有准备工作做好,开始按照上图思路编写代码,在QuestionServiceImpl类中添加代码:

 package cn.tedu.knows.portal.service.impl;
 
 import cn.tedu.knows.portal.mapper.UserMapper;
 import cn.tedu.knows.portal.model.Question;
 import cn.tedu.knows.portal.mapper.QuestionMapper;
 import cn.tedu.knows.portal.model.Tag;
 import cn.tedu.knows.portal.model.User;
 import cn.tedu.knows.portal.service.IQuestionService;
 import cn.tedu.knows.portal.service.ITagService;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
 /**
  * <p>
  * 服务实现类
  * </p>
  *
  * @author tedu.cn
  * @since 2021-08-23
  */
 @Service
 public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements IQuestionService {
     //根据用户名查询用户用
     @Autowired
     UserMapper userMapper;
 
     //查询问题用
     @Autowired
     QuestionMapper questionMapper;
 
     //重写方法
     @Override
     public List<Question> getMyQuestions(String username) {
         //1.根据用户名查询用户信息
         User user = userMapper.findUserByUsername(username);
         //2.根据用户信息进行查询操作(QueryWrapper查询)
         QueryWrapper<Question> query = new QueryWrapper<>();
         query.eq("user_id",user.getId());//根据用户id查
         query.eq("delete_status",0);//并且不能是删除的
         //设置时间降序排列
         query.orderByDesc("createtime");
         //3.执行查询
         List<Question> questions = questionMapper.selectList(query);
         //4.为当前List中的所有question对象的tags属性赋值
         for (Question q:questions) {
             //根据q对象的tagNames属性获得对应的List<Tag>
             List<Tag> tags = tagNamesToTags(q.getTagNames());
             q.setTags(tags);
        }
         //5.返回查询结果
         return questions;
    }
 
     @Autowired
     private ITagService tagService;
 
     //根据当前Question的tagNames属性,获得对应的List<Tag>集合
     private List<Tag> tagNamesToTags(String tagNames){
         //tagNames:"Java基础,Java SE,面试题"---一个字符串
         //将tagNames拆分成字符串数组
         String[] names = tagNames.split(",");
         //names:{"Java基础","Java SE","面试题"}
         Map<String,Tag> map = tagService.getTagMap();
         //声明一个List用于接收对应的标签对象
         List<Tag> tags = new ArrayList<>();
         //遍历标签名称数组
         for (String name:names){
             tags.add(map.get(name));
        }
         return tags;//返回每个带有标签名的对象
    }
 }
 

Vue绑定显示问题标签:index_student.html 216行附近

 <a class="text-info badge badge-pill bg-light"
    href="tag/tag_question.html"
     v-for="tag in question.tags">
   <small v-text="tag.name" >Java基础 &nbsp;</small></a>

重启服务,进行测试,可以查看到每个问题对应的标签名。

1.6 显示问题配图

  我们每个问题的右侧都有配图,是为了看起来更美观。我们的每个问题至少有一个标签,使用当前问题的第一个标签的id来指定提供的图片,index.js文件中已经编写好了显示图片的路径的代码,我们只需要在页面上进行绑定即可。

在index_student.html 237行附近修改代码:

 <!-- / class="media-body"-->
 <img src="img/tags/example0.jpg"
      class="ml-3 border img-fluid rounded" alt="" width="208" height="116"
      :src="question.tagImage">

1.7 实现问题列表分页

1.7.1 为什么需要分页?

  所谓分页,就是数据显示并不是一次查询显示全部,而是按照指定的页面大小,每次显示一部分,可以有多页数据。当查询出的数据量较大时,如果一次查询全部信息,服务器\客户端\流量都会较大。而且通常情况下,用户只会关注前面的数据,后面的数据查询是无意义的,所以使用分页来优化查询。

分页的好处:

  1. 流量小

  2. 服务器每次只查询一页数据

  3. 客户端显示压力小

  4. 将最可能被用户使用的数据优先显示,提高查询效率

  5. 方便用户记忆和查询

1.7.2 数据库实现分页查询的原理

使用limit关键字实现分页:

 SELECT * FROM question WHERE user_id=11 ORDER BY createtime desc LIMIT  0,8

  limit关键字的含义是从查询的结果中截取指定位置的行显示(数据库先进行安排,安排好后直接将查询到的内容返回,并不是全查后,再截取),现在st2用户查询的分页结果如图:

limit格式: limit 从第n条后开始显示,要显示的条数(最多显示)

1.7.3 使用PageHelper实现分页查询

1.概述

  我们明确了分页查询原理,但是自己写的话,还是需要很多额外的工作量的。企业开发中基本不会自己编写和计算分页的信息,因为市面上有很多分页的框架或插件。PageHelper是支持mybatis框架的分页插件,因为PageHelper也没有在SpringBoot中定义版本,所以我们要在父项目中定义版本。

父项目的pom.xml新增版本控制:

 <properties>
     <java.version>1.8</java.version>
     <mybatis.plus.version>3.3.1</mybatis.plus.version>
    <!-- ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ -->
<pagehelper.starter.version>1.3.0</pagehelper.starter.version>
 </properties>
 <dependencyManagement>
     <dependencies>
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-extension</artifactId>
             <version>${mybatis.plus.version}</version>
         </dependency>
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
             <version>${mybatis.plus.version}</version>
         </dependency>
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-generator</artifactId>
             <version>${mybatis.plus.version}</version>
         </dependency>
         <!-- ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ -->
         <dependency>
             <groupId>com.github.pagehelper</groupId>
             <artifactId>pagehelper-spring-boot-starter</artifactId>
             <version>${pagehelper.starter.version}</version>
         </dependency>
     </dependencies>
 </dependencyManagement>

子项目的pom.xml文件中添加依赖:

 <dependency>
     <groupId>com.github.pagehelper</groupId>
     <artifactId>pagehelper-spring-boot-starter</artifactId>
 </dependency>

2.基本使用

  PageHelper的使用非常简单,不需要对现有的任何查询进行任何修改,只需要在想分页的查询执行之前添加如下代码,在QuestionServiceImpl.java添加添加代码:

 //3.执行查询
 //startPage方法的参数我们写两个,第一个是要查询第几页,如果想查询第一页就写1,以此类推
 // 第二个参数是当前页最多显示多少条
 // 这个代码一旦编写,会在下一次进行的查询时自动添加limit关键字,以及具体的截取区间
 PageHelper.startPage(1,8);//注意:后面的参数与limit不一样
 List<Question> questions=questionMapper.selectList(query);

1.7.4 分页信息类PageInfo

  我们的分页查询并不是简单的查询出想要的数据显示在页面上,同时还要支持上一页\下一页\第几页这样的查询需求,那么我们就需要保存当前的分页信息,例如如下数据:

  • 总条数

  • 总页数

  • 当前页有没有上一页\下一页

  还有各种数据,如果我们自己查询和计算的话,又需要写很多代码。PageHelper提供了一个PageInfo类型,在我们进行分页查询后,能自动计算上述以及分页需要的各种信息的数据,页面需要这样的数据,我们把PageInfo返回即可。

PageInfo类所有属性介绍:

 //当前页
 private int pageNum;
 //每页的数量
 private int pageSize;
 //当前页的行数量
 private int size;
 //当前页面第一个元素在数据库中的行号
 private int startRow;
 //当前页面最后一个元素在数据库中的行号
 private int endRow;
 //总页数
 private int pages;
 //前一页页号
 private int prePage;
 //下一页页号
 private int nextPage;
 //是否为第一页
 private boolean isFirstPage;
 //是否为最后一页
 private boolean isLastPage;
 //是否有前一页
 private boolean hasPreviousPage;
 //是否有下一页
 private boolean hasNextPage;
 //导航条中页码个数
 private int navigatePages;
 //所有导航条中显示的页号
 private int[] navigatepageNums;
 //导航条上的第一页页号
 private int navigateFirstPage;
 //导航条上的最后一页号
 private int navigateLastPage;

修改查询用户问题列表实现分页功能,在IQuestionService接口修改查询问题列表的方法:

 package cn.tedu.knows.portal.service;
 
 import cn.tedu.knows.portal.model.Question;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.github.pagehelper.PageInfo;
 
 import java.util.List;
 
 /**
  * <p>
  * 服务类
  * </p>
  *
  * @author tedu.cn
  * @since 2021-08-23
  */
 public interface IQuestionService extends IService<Question> {
     //根据当前登录用户查询所有问题列表
     //List<Question> getMyQuestions(String username);
     //PageInfo保存分页信息和查询出来的问题列表
     PageInfo<Question> getMyQuestions(String username,Integer pageNum,Integer pageSize);
 }
 

修改实现类QuestionServiceImpl的getMyQuestions方法:

 @Override
 //     ↓↓↓↓↓↓↓↓↓↓
 public PageInfo<Question> getMyQuestions(String username,
                     Integer pageNum,Integer pageSize) {
     //             ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
     //1.根据用户名查询用户信息
     User user=userMapper.findUserByUsername(username);
     //2.根据用户信息进行查询操作(QueryWrapper查询)
     QueryWrapper<Question> query=new QueryWrapper<>();
     query.eq("user_id",user.getId());
     query.eq("delete_status",0);
     //设置时间降序排序
     query.orderByDesc("createtime");
     //3.执行查询
     //startPage方法的参数我们写两个,第一个是要查询第几页,如果想查询第一页就写1
     // 第二个参数是当前页最多显示多少条
     // 这个代码一旦编写,会在下一次进行的查询时自动添加limit关键字,以及具体的截取区间
     PageHelper.startPage(pageNum,pageSize);//注意:后面的参数与limit不一样
     // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
     List<Question> questions=questionMapper.selectList(query);
     //4.为当前List中的所有question对象的tags属性赋值
     for(Question q:questions){
         //根据q对象的tagNames属性获得对应的List<Tag>
         List<Tag> tags=tagNames2Tags(q.getTagNames());
         q.setTags(tags);
    }
     //5.返回查询结果
     // 千万别忘了返回!!!
     return new PageInfo<>(questions);
     //       ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
 }

控制器QuestionController修改代码:

 package cn.tedu.knows.portal.controller;
 
 
 import cn.tedu.knows.portal.model.Question;
 import cn.tedu.knows.portal.service.IQuestionService;
 import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
 
 /**
  * <p>
  * 前端控制器
  * </p>
  *
  * @author tedu.cn
  * @since 2021-08-23
  */
 @RestController
 //@RequestMapping("/portal/question")
 @RequestMapping("/v1/questions") //微服务框架标准
 public class QuestionController {
     //查询当前用户问题列表
     @Autowired
     private IQuestionService questionService;
 
     //根据当前登录用户,查询用户问题列表的控制层方法
     @GetMapping("/my") //localhost:8080/v1/questions/my
     //参数中可以使用一个注解获得当前登录用户的信息(从Spring Security中获取)
     //public List<Question> my(@AuthenticationPrincipal UserDetails user){
     public PageInfo<Question> my(@AuthenticationPrincipal UserDetails user,Integer pageNum){
         //避免pageNum为null,导致空指针异常
         if(pageNum==null){
             pageNum=1;
        }
         //List<Question> questions = questionService.getMyQuestions(user.getUsername());
         PageInfo<Question> pageInfo = questionService.getMyQuestions(user.getUsername(),pageNum,8);
         //返回查询到的所有问题
         //return questions;
         return pageInfo;
    }
 }
 

重启服务,发送同步请求测试:http://localhost:8080/v1/questions/my,如能正常查询出json格式,表示一切正常。

1.7.5 显示问题列表并分页

  上面查询出了数据和之前返回的list数据结构不同了(相当于封装了两层list),所以要显示问题列表必须修改index.js的绑定,在index.js中axios方法的.then代码修改如下:

 .then(function(r){
     console.log("成功加载数据");
     console.log(r);
     if(r.status == OK){
         // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓           ↓↓↓↓
         questionsApp.questions = r.data.list;//r.data指的是PageInfo,要调用里面的list
         questionsApp.pageinfo = r.data;
         // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
         //为question对象添加持续时间属性
         questionsApp.updateDuration();
         questionsApp.updateTagImage();
    }
 })

index_student.html 245行附近添加分页的Vue代码绑定:

 <div class="pagination">
  <!--.prevent阻止a标签页面跳转,页面局部更新-->
  <a class="page-item page-link" href="#"
  @click.prevent="loadQuestions(pageinfo.prePage)">上一页</a>
  <!--遍历数组、跳转到第n页、显示第n页、深色背景、浅色字(点击指定页码时)-->
  <a class="page-item page-link " href="#"
  v-for="n in pageinfo.navigatepageNums"
  @click.prevent="loadQuestions(n)"
  v-text="n"
  :class="{'bg-secondary text-light':n == pageinfo.pageNum}">1</a>
  <a class="page-item page-link" href="#"
  @click.prevent="loadQuestions(pageinfo.nextPage)">下一页</a>
 </div>

重启服务,测试分页效果:注意第一页前一页无反应,最后一页下一页为第一页

 点击第1页PageInfo信息:分别对应sql语句和页面信息,最下面显示当前页显示问题的条数

 点击第2页PageInfo信息:

 点击第3页PageInfo信息:

 

2.补充:业务开发顺序

2.1 查询业务

开发顺序:

  1.数据访问层

  2.业务逻辑层(先接口,后实现类)

  3.控制层

  4.页面Vue绑定,js代码

2.2 新增业务

开发顺序:

  1.确定\创建Vo对象

  2.Vue绑定和js相关代码

  3.编写控制器(编写完可以测试控制器接收表单信息)

  4.数据访问层

  5.业务逻辑层

  6.控制器调用业务逻辑层

 

posted @ 2021-08-26 22:30  Coder_Cui  阅读(155)  评论(0编辑  收藏  举报