超详细!利用SpringBoot+SpringCloud做一个问答项目(十五)

目录

一、服务器端向客户端响应“问题”的标签列表数据【续】

二、显示“我的问答”分页链接

三、显示问题详情--显示页面

四、显示问题详情--持久层

五、显示问题详情--业务层

六、显示问题详情--控制器层

七、显示问题详情--前端页面

一、服务器端向客户端响应“问题”的标签列表数据【续】


当已经把各标签数据存入到Redis中,则在需要获取标签数据时,直接从Redis中获取即可!

目前,服务器端响应到客户端的数据中交不包含“标签列表”,只有各“问题”对应的“标签的id的列表”:

所以,首先,需要在QuestionListItemVO中添加新的属性,以表示“标签列表”:

由于设计为“从Redis中获取标签列表数据”,所以,获取用户的问题列表的持久层的代码是不需要改动的!在处理“查询某用户的问题列表”的业务层中补充代码,则在QuestionServiceImpl 中,先声明Redis工具类对象:

然后,在getMyQuestions()方法中:

直接执行原有的业务层单元测试,测试的输出结果例如:

PageInfo{
	pageNum=1, 
	pageSize=3, 
	size=3, 
	startRow=1, 
	endRow=3, 
	total=15, 
	pages=5, 
	list=Page{
		count=true, 
		pageNum=1, 
		pageSize=3, 
		startRow=0, 
		endRow=3, 
		total=15, 
		pages=5, 
		reasonable=false, 
		pageSizeZero=false
	}[....], 
	prePage=0, 
	nextPage=2, 
	isFirstPage=true, 
	isLastPage=false, 
	hasPreviousPage=false, 
	hasNextPage=true, 
	navigatePages=8, 
	navigateFirstPage=1, 
	navigateLastPage=5, 
	navigatepageNums=[1, 2, 3, 4, 5]
}

并且,再次通过浏览器直接访问控制器层,可以看到响应的结果中已经包含标签列表:

最后,在处理页面时,将原本临时添加的设置模拟数据的标签列表删除即可:

二、显示“我的问答”分页链接


先在index.js的Vue对象中,添加模拟的分页数据:

然后,在index.html中使用以上属性进行遍历并显示:

完成后,重新启动straw-gateway项目,在“我的问答”列表下方可以看到分页链接:

为了完善页面显示效果及功能,还应该在index.js中添加属性:

index.html页面中配置:

然后,在index.js中,当从服务器端获取到数据后,更新以上Vue属性的值:

全部完成后,重启straw-gateway项目,分页功能是可以正常使用的!

三、显示问题详情--显示页面


目前,点击主页的各“问题”的标题,应该显示“问题详情”页,但目前显示的结果是404,因为在static下并没有对应的页面,页面文件已经移动到templates文件夹下:

可以在SystemController中添加对/question/detail.html路径请求的处理,以显示该页面:

完成后,重启straw-gateway项目,在主页中点击任意“问题”的标题,即可打开“问题详情”页面(页面中显示的数据都是在HTML中设计的模拟数据)。

四、显示问题详情--持久层


显示某问题的详情,需要执行的SQL语句大致是:

select * from question where id=? and is_delete=0

以上查询结果中并不包含“问题”的“标签列表”,而最终响应到客户端的数据是需要包含“标签列表”的!则先在straw-commonscn.tedu.straw.commons.vo包中创建QuestionDetailVO类,该类的属性与Question的相同,并在其基础上添加List<TagVO> tags属性用于表示“标签列表”:

@Data
@Accessors(chain = true)
public class QuestionDetailVO implements Serializable {​    
    private Integer id;    
    private String title;    
    private String content;    
    private Integer userId;    
    private String userNickName;    
    private Integer status;    
    private Integer hits;    
    private Integer isDelete;    
    private String tagIds;    
    private LocalDateTime gmtCreate;    
    private LocalDateTime gmtModified;    
    private List<TagVO> tags;​}

QuestionMapper.java接口中添加抽象方法:

/**
 * 根据id查询某“问题”的详情
 *
 * @param id “问题”的id
 * @return 匹配“问题”的详情,如果没有匹配的数据,则返回null
 */
QuestionDetailVO findById(Integer id);

QuestionMapper.xml中配置以上抽象方法映射的SQL语句:

<resultMap id="QuestionDetailMap" type="cn.tedu.straw.commons.vo.QuestionDetailVO">
    <id column="id" property="id" />
    <result column="title" property="title" />
    <result column="content" property="content" />
    <result column="user_id" property="userId" />
    <result column="user_nick_name" property="userNickName" />
    <result column="status" property="status" />
    <result column="hits" property="hits" />
    <result column="is_delete" property="isDelete" />
    <result column="tag_ids" property="tagIds" />
    <result column="gmt_create" property="gmtCreate" />
    <result column="gmt_modified" property="gmtModified" />
</resultMap>

<select id="findById" resultMap="QuestionDetailMap">
    SELECT * FROM question WHERE id=#{id}
</select>

QuestionMapperTests中编写并执行单元测试:

@Test
void findById() {
    Integer id = 10;
    QuestionDetailVO question = mapper.findById(id);
    log.debug("question >>> {}", question);
}

五、显示问题详情--业务层


先创建“问题数据不存在”的异常:

public class QuestionNotFoundException extends ServiceException {}

IQuestionService 接口中添加抽象方法:

/**
 * 根据id查询某“问题”的详情
 *
 * @param id “问题”的id
 * @return 匹配“问题”的详情
 */
QuestionDetailVO getQuestionDetail(Integer id);

QuestionServiceImpl类中实现以上抽象方法:

@Override
public QuestionDetailVO getQuestionDetail(Integer id) {
    // 调用持久层的功能查询数据
    QuestionDetailVO question = questionMapper.findById(id);
    // 判断查询结果是否为null
    if (question == null) {
        // 是:抛出QuestionNotFoundException
        throw new QuestionNotFoundException("获取问题详情失败!尝试访问的数据不存在!");
    }

    // 补全tags属性的值
    String tagIdsString = question.getTagIds(); // 6, 10, 15
    String[] tagIdsArray = tagIdsString.split(", "); // {6, 10, 15}
    List<TagVO> tags = new ArrayList<>();
    for (int i = 0; i < tagIdsArray.length; i++) {
        String key = "tag" + tagIdsArray[i]; // tag6
        TagVO tag = (TagVO) redisUtils.get(key); // { id:6, name: 'WEB' }
        tags.add(tag);
    }
    question.setTags(tags);

    // 返回查询结果
    return question;
}

完成后,在QuestionServiceTests中测试:

@Test
void getQuestionDetail() {
    try {
        Integer id = 10000;
        QuestionDetailVO question = service.getQuestionDetail(id);
        log.debug("question >>> {}", question);
    } catch (ServiceException e) {
        log.debug("查询问题详情失败,错误类型:{}", e.getClass().getName());
        log.debug("错误原因:{}", e.getMessage());
    }
}

六、显示问题详情--控制器层


由于在业务层创建并抛出了新的异常,需要在R.State中补充对应的状态码,并在GlobalExceptionHandler中进行处理。

QuestionController中添加处理请求的方法:

// http://localhost:8081/v1/questions/15
// http://localhost/api-question/v1/questions/15
@GetMapping("/{id}")
public R<QuestionDetailVO> showQuestionDetail(@PathVariable("id") Integer id) {
    return R.ok(questionService.getQuestionDetail(id));
}

完成后,重启straw-api-question项目,在浏览器中直接访问以上测试路径,观察是否得到正确的结果。

七、显示问题详情--前端页面


detail.html中,先确定“显示问题详情”的区域,配置id

static/js/question文件夹下创建detail.js文件,在该文件中创建Vue对象:

并在detail.html中引用该文件:

然后,在Vue对象中添加测试的模拟数据:

并在页面中绑定这些数据属性以显示模拟数据:

接下来,需要将以上测试数据改为由服务器端响应的数据,由于服务器端响应的时间不是例如“3天前”这样的格式,所以,需要事先引用此前的get_time_text.js文件:  

为了保证在detail.html中能够正确的显示此前所点击的问题的详情,需要在此前点击问题时把问题的id作为参数传递到detail.html页面,推荐将id放在URL中,便于实现URL分享(将URL复制发送给他人,也可以看到相同的页面),所以,在index.html中:

然后,在detail.js中,加载“问题”详情时,先从URL中获取id值,并简单的判断id值的格式是否有误,然后,基于该id从服务器端获取数据,获取的数据中并不包含“发表时间”的字符串文本,所以,还需要处理时间文本,最后,更新Vue属性即可:

loadQuestionDetail: function () {
    // alert('准备加载问题详情……');
    let id = location.search.substring(1);
    if (id == "" || isNaN(id) || id < 1) {
        alert('参数错误!');
        location.href = '/index.html';
        return;
    }
    $.ajax({
        url: '/api-question/v1/questions/' + id,
        success: function (json) {
            if (json.state == 2000) {
                let question = json.data;
                question.gmtCreateText = getTimeText(question.gmtCreate);
                questionDetailApp.question = question;
            } else {
                alert(json.message);
                location.href = '/index.html';
            }
        }
    });
}

完成后,重启straw-gateway项目,刷新主页index.html,点击“我的问答”中任何问题的标题,都可以打开详情页面显示该“问题”。

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值