【开源项目--稻草】Day06

【开源项目--稻草】Day06

  • 1. 学生提问与解答功能
  • 2. 显示create.html
    • 2.1 HomeController中代码
    • 2.2 复用网页的标签导航条
  • 3. 创建问题发布界面
    • 3.1 富文本编辑器
  • 4.多选下列框
  • 5.动态加载所有标签和老师
  • 6. 发布问题的业务处理

1. 学生提问与解答功能

在这里插入图片描述
学生提问:

提问时指定标签和回答问题的老师

讲师回复:

指定讲师登录系统后可以对学员的提问进行回复

评论:

学员收到讲师回复后可以对回复进行评论(追问)

讲课也可以进行评论(追答或补充)

问题状态:

学生刚提问时为:未回复

讲师回复后为:已回复

问题解决后为:已解决

  • 问题怎么能称为解决?
  1. 学员标记为解决状态
  2. 讲师可以将问题标记为解决
  3. 问题超过一定时间,自动解决

我们先开发的模块是学员的问题发布功能
在这里插入图片描述

2. 显示create.html

将static/question/create.html

复制到

templates/question/create.html

并编写控制器代码显示这个页面

2.1 HomeController中代码

   // 显示学生问题发布页面@GetMapping("/question/create.html")public ModelAndView createQuestion(){//templates/question/create.htmlreturn new ModelAndView("question/create");}

2.2 复用网页的标签导航条

我们在index.html页面中已经开发过显示数据库中所有标签到页面的导航条中的代码

现在create.html又需要这样的功能,难道我们要再开发一次吗? 显然不是的

我们可以使用Thymeleaf提供的fragment模板来替换当前页面的内容

使用步骤

步骤1:

定义模板

在index.html页面中,将要复用的html区域用特定标签标记

th:fragment=“xxx”

<div class="container-fluid"  th:fragment="tags_nav" ><!-- 代码略 -->
</div>

步骤2:

套用模板

现在是create.html需要复用代码,所以是这个页面套用模板

th:replace="xxx"来套用

保证页面支持th:的写法不报错

<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">

套用模板

<div class="container-fluid" th:replace="index::tags_nav" ><div class="nav font-weight-light"><!-- 代码略 --></div>
</div>

其中th:replace="index::tags_nav"的意思是

用index.html页面中名为tags_nav的模板中的代码替换掉当前编写套用标记的html标签

最后在代码临结束之前,引入ajax和Vue代码

</body>
<script src="../js/utils.js"></script>
<script src="../js/tags_nav.js"></script>
</html>

3. 创建问题发布界面

3.1 富文本编辑器

富文本编辑器适用于那些需要格式甚至是图片的用户输入需求

这样的编辑器都是基于标签的

只是在这个标签的基础上添加了很多js代码或相关插件的实现,我们无需手动开发

市面上有很多功能全面的免费的富文本编辑器工具,其中比较流行的就是我们使用的这个

summernote官方网站是:www.summernote.org

下载它的支持后再页面上编写如下代码引入样式和js文件

<link rel="stylesheet" href="../bower_components/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="../bower_components/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../bower_components/summernote/dist/summernote-bs4.min.css"><script src="../bower_components/jquery/dist/jquery.min.js"></script>
<script src="../bower_components/popper.js/dist/umd/popper.min.js"></script>
<script src="../bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="../bower_components/polyfill/dist/polyfill.min.js"></script>
<script src="../bower_components/summernote/dist/summernote-bs4.js"></script>
<script src="../bower_components/summernote/dist/lang/summernote-zh-CN.min.js"></script>

在页面中需要富文本编辑器的位置编写如下代码

<textarea name="content" id="summernote"></textarea>

最终通过编写js代码来开启这个编辑器的效果

<script>$(document).ready(function() {$('#summernote').summernote({height: 300,  //高度tabsize: 2,   //tab大小lang: 'zh-CN',//中文支持placeholder: '请输入问题的详细描述...'});});
</script>

一般这个代码在页面最后位置

4.多选下列框

网页中的下拉列表框是一个能够多选,并且有选中后样式的功能的控件

这个控件是由Vue提供的插件Vue-Select实现的

官方网站是 https://vue-select.org/

依赖JQuery同时也依赖Vue核心的js

除此之外还需要引入一些依赖

<link rel="stylesheet" href="../bower_components/vue-select/dist/vue-select.css">
<script src="../bower_components/vue/dist/vue.js"></script>
<script src="../bower_components/vue-select/dist/vue-select.js"></script>

在这里插入图片描述
在create.html的form表单中找到选择标签和老师的下拉框

将他们的代码修改为:

<!-- 这个id是自己添加的!!!注意!!!!  -->
<div class="col-8" id="createQuestionApp"><h4 class="border-bottom m-2 p-2 font-weight-light"><i class="fa fa-question-circle-o" aria-hidden="true"></i>填写问题</h4><form ><div class="form-group"><label for="title">标题:</label><input type="text" class="form-control" id="title" name="title" placeholder="请填写标题3~50字符"pattern="^.{3,50}$" required v-model="title"></div><div class="form-group"><label >请至少选择一个标签:</label><v-select multiple required v-bind:options="tags"v-model="selectedTags" placeholder="请选择问题的标签"></v-select></div><div class="form-group"><label >请选择老师:</label><v-select multiple requiredv-bind:options="teachers"v-model="selectedTeachers"placeholder="请选择回答的老师"></v-select></div><div class="form-group"><!--富文本编辑器 start--><label for="summernote">问题正文</label><textarea name="content" id="summernote"></textarea><!--富文本编辑器 end--></div><button type="submit" class="btn btn-primary mt-3">提交问题</button></form></div>

上面表单修改完成后,js文件中指定createQuestion.js引用添加依赖

</body>
<script src="../js/utils.js"></script>
<script src="../js/tags_nav.js"></script>
<script src="../js/createQuestion.js"></script>
</html>

createQuestion.js文件中的内容

不连接数据库可以写为:

Vue.component('v-select', VueSelect.VueSelect);
let createQuestionApp = new Vue({el:'#createQuestionApp',data:{title:'',selectedTags:[],tags:['Java基础','Java OOP', 'Java SE'],selectedTeachers:[],teachers:['范传奇', '王克晶''刘国斌']}
});

测试可以发现,我们成功的可以选择做个标签和老师了

下面的工作就是从数据库加载所有标签和老师

5.动态加载所有标签和老师

动态加载所有标签的实现非常简单,因为我们直接可以调用现成的控制器方法

返回所有Tag的集合

createQuestion.js中编写代码如下

//启动v-select标签
Vue.component("v-select", VueSelect.VueSelect);
let createQuestionApp = new Vue({el: "#createQuestionApp",data: {title:"",selectedTags:[],tags:[],selectedTeachers:[],teachers:["苍老师","范老师","克晶老师"]},methods: {loadTags:function(){$.ajax({url:"/v1/tags",method:"get",success:function(r){console.log(r);if(r.code==OK){let list=r.data;//获得所有标签数组//list=[{id:1,name:"java基础"},{...},{...}]let tags=[];for(let i=0;i<list.length;i++){//push方法表示向这个数组的最后位置添加元素//效果和java中list的add方法一致tags.push(list[i].name);}console.log(tags);createQuestionApp.tags=tags;}}});}},created:function(){this.loadTags();}
});

动态加载所有老师

实现步骤

步骤1:

添加业务逻辑层接口方法IUserService

	// 查询所有老师用户的方法List<User> getMasters();

步骤2:

实现这个业务逻辑层接口的方法UserServiceImpl

    @Overridepublic List<User> getMasters() {QueryWrapper<User> query=new QueryWrapper<>();query.eq("type",1);List<User> list=userMapper.selectList(query);return list;}

步骤3:

编写控制层:UserController中设计路径v1/users/master,返回R<List>即可

@RestController
@RequestMapping("/v1/users")
public class UserController {@AutowiredIUserService userService;@GetMapping("/master")public R<List<User>> master(){List<User> masters=userService.getMasters();return R.ok(masters);}
}

步骤4:

参照绑定所有标签的Vue代码绑定所有老师即可

//启动v-select标签
Vue.component("v-select", VueSelect.VueSelect);
let createQuestionApp = new Vue({el: "#createQuestionApp",data: {title:"",selectedTags:[],tags:[],selectedTeachers:[],teachers:["苍老师","范老师","克晶老师"]},methods: {loadTags:function(){$.ajax({url:"/v1/tags",method:"get",success:function(r){console.log(r);if(r.code==OK){let list=r.data;//获得所有标签数组//list=[{id:1,name:"java基础"},{...},{...}]let tags=[];for(let i=0;i<list.length;i++){//push方法表示向这个数组的最后位置添加元素//效果和java中list的add方法一致tags.push(list[i].name);}console.log(tags);createQuestionApp.tags=tags;}}});},loadTeachers:function(){$.ajax({url:"/v1/users/master",method:"get",success:function(r){console.log(r);if(r.code==OK){let list=r.data;//获得所有讲师数组let teachers=[];for(let i=0;i<list.length;i++){//push方法表示向这个数组的最后位置添加元素//效果和java中list的add方法一致teachers.push(list[i].nickname);}console.log(teachers);createQuestionApp.teachers=teachers;}}});}},created:function(){this.loadTags();this.loadTeachers();}
});

6. 发布问题的业务处理

我们先来完成数据提交到控制器的内容

步骤1:

为了这次提交新建一个Vo类 QuestionVo

@Data
public class QuestionVo implements Serializable {@NotBlank(message = "标题不能为空")@Pattern(regexp = "^.{3,50}$",message = "标题长度在3~50个字符之间")private String title;private String[] tagNames={};private String[] teacherNickNames={};@NotBlank(message = "问题内容不能为空")private String content;}

步骤2:

开发发布问题的控制器代码

在QuestionController中代码如下

	// 学生发布问题的控制器方法@PostMappingpublic R createQuestion(@Validated QuestionVo questionVo,BindingResult result){if(result.hasErrors()){String message=result.getFieldError().getDefaultMessage();log.warn(message);return R.unproecsableEntity(message);}if(questionVo.getTagNames().length==0){log.warn("必须选择至少一个标签");return R.unproecsableEntity("必须选择至少一个标签");}if(questionVo.getTeacherNickNames().length==0){log.warn("必须选择至少一个老师");return R.unproecsableEntity("必须选择至少一个老师");}//这里应该将vo对象交由service层去新增log.debug("接收到表单数据{}",questionVo);return R.ok("发布成功!");}

步骤3:

找到create.html的form标签

使用v-on:submit.prevent绑定提交事件 .prevent是阻止表单提交用的

<form v-on:submit.prevent="createQuestion">

步骤4:

在createQuestion.js

文件中新增createQuestion方法

并在方法中收集要提交的信息,最后使用ajax提交到控制器

 createQuestion:function(){let content=$("#summernote").val();console.log(content);//定义一个data对象,用于ajax提交信息到控制器let data={title:this.title,tagNames:this.selectedTags,teacherNickNames:this.selectedTeachers,content:content}console.log(data);$.ajax({url:"/v1/questions",traditional:true,//使用传统数组的编码方式,SpringMvc才能接收method:"post",data:data,success:function(r){console.log(r)if(r.code== OK){console.log(r.message);}else{console.log(r.message);}}});}

下面我们需要完成新增问题的业务逻辑的开发

首先来了解一下我们需要什么操作才能完成这个业务
在这里插入图片描述
举例
在这里插入图片描述
步骤1:

讲师的信息也是可以保存在换存中来避免多次访问数据库来提交运行效率的

所以我们参照对标签的处理方法,对所有讲师也进行缓存

IUserService中

	// 查询所有老师用户的方法List<User> getMasters();//查询所有老师用户的Map方法Map<String,User> getMasterMap();

步骤2

参照TagServiceImpl中对标签的缓存,处理讲师缓存

UserServiceImpl代码如下

private final List<User> masters=new CopyOnWriteArrayList<>();private final Map<String,User> masterMap=new ConcurrentHashMap<>();private final Timer timer=new Timer();//初始化块:在构造方法运行前开始运行{timer.schedule(new TimerTask() {@Overridepublic void run() {synchronized (masters){masters.clear();masterMap.clear();}}},1000*60*30,1000*60*30);}
​
​
​@Overridepublic List<User> getMasters() {if(masters.isEmpty()){synchronized (masters){if(masters.isEmpty()){QueryWrapper<User> query=new QueryWrapper<>();query.eq("type",1);//将所有老师缓存masters集合中masters.addAll(userMapper.selectList(query));for(User u: masters){masterMap.put(u.getNickname(),u);}//脱敏:将敏感信息从数组(集合\map)中移除for(User u: masters){u.setPassword("");}}}}return masters;}@Overridepublic Map<String, User> getMasterMap() {if(masterMap.isEmpty()){getMasters();}return masterMap;}

步骤3:

编写IQuestionService接口中发布问题的方法

步骤4:

在QuestionServiceImpl类中实现接口中定义的方法

业务的步骤大概为

// 获取当前登录用户信息(可以验证登录情况)// 将该问题包含的标签拼接成字符串以","分割 以便添加tag_names列// 构造Question对象// 新增Question对象// 处理新增的Question和对应Tag的关系// 处理新增的Question和对应User(老师)的关系

代码如下

@AutowiredQuestionTagMapper questionTagMapper;@Overridepublic void saveQuestion(QuestionVo questionVo) {log.debug("收到问题数据{}",questionVo);// 获取当前登录用户信息(可以验证登录情况)String username=userService.currentUsername();User user=userMapper.findUserByUsername(username);// 将该问题包含的标签拼接成字符串以","分割 以便添加tag_names列StringBuilder bud=new StringBuilder();for(String tag : questionVo.getTagNames()){bud.append(tag).append(",");}//删除最后一个","bud.deleteCharAt(bud.length()-1);String tagNames=bud.toString();// 构造Question对象Question question=new Question().setTitle(questionVo.getTitle()).setContent(questionVo.getContent()).setUserId(user.getId()).setUserNickName(user.getUsername()).setTagNames(tagNames).setCreatetime(LocalDateTime.now()).setStatus(0).setPageViews(0).setPublicStatus(0).setDeleteStatus(0);// 新增Question对象int num=questionMapper.insert(question);if(num!=1){throw  new ServiceException("服务器忙!");}log.debug("保存了对象:{}",question);// 处理新增的Question和对应Tag的关系Map<String,Tag> name2TagMap=tagService.getName2TagMap();for(String tagName : questionVo.getTagNames()){//根据本次循环的标签名称获得对应的标签对象Tag tag=name2TagMap.get(tagName);//构建QuestionTag实体类对象QuestionTag questionTag=new QuestionTag().setQuestionId(question.getId()).setTagId(tag.getId());//执行新增num=questionTagMapper.insert(questionTag);if(num!=1){throw new ServiceException("数据库忙!");}log.debug("新增了问题和标签的关系:{}",questionTag);}// 处理新增的Question和对应User(老师)的关系}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/79791.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VBA遍历Wrod所有表格每个单元格,单元格未尾两个回车替换

一、遍历 word中遍历所有表格的每个单元格。因为在单元格时会常出错。浪费了不少时间。 Sub a()Dim doc As Document, tb As Table, ce As cellDim rng As Range, p As ParagraphSet doc ActiveDocumentFor Each tb In doc.TablesFor Each ce In tb.Range.Cells 关键处就是这里…

Java中的Unsafe类详解

Java中的Unsafe类详解 1. Unsafe 概念2. Unsafe 构造及获取3. 功能和应用3.1 内存管理3.1.1 普通读写3.1.2 volatile 读写3.1.3 有序读写3.1.4 直接操作内存 3.2 CAS3.3 偏移量3.4 线程调度3.5 类加载3.6 内存屏障3.7 其他操作 4. 潜在风险和挑战5. 最佳实践5.1 使用案例&#…

QtAV for ubuntu16.04

下载ubuntu https://releases.ubuntu.com/16.04/ubuntu-16.04.7-desktop-amd64.iso 下载ffmpeg https://ffmpeg.org/download.html 下载QtAV https://github.com/wang-bin/QtAV/releases 更新 sudo apt update 安装库 sudo apt-get install libglu1-mesa-dev freeglut3-dev…

【算法系列 | 7】深入解析查找算法之—布隆过滤器

序言 心若有阳光&#xff0c;你便会看见这个世界有那么多美好值得期待和向往。 决定开一个算法专栏&#xff0c;希望能帮助大家很好的了解算法。主要深入解析每个算法&#xff0c;从概念到示例。 我们一起努力&#xff0c;成为更好的自己&#xff01; 今天第3讲&#xff0c;讲一…

【数据结构】链表(一)

链表&#xff08;一&#xff09; 文章目录 链表&#xff08;一&#xff09;01 引入02 概念及结构03 单向不带头不循环链表实现3.1 创建节点类型3.2 简易创建一个链表3.3 遍历链表每个节点3.4 获取链表长度3.5 查找是否包含关键字key是否在单链表当中3.6 头插法3.7 尾插法3.8 任…

MySQL 重置root 密码

5.7 版本 首先要把服务mysql57 关闭 net stop MySQL57 在安装的mysql57的程序的bin中 运行cmd&#xff08;管理员运行&#xff09; mysqld --defaults-file‘mysql存放数据的位置\my.ini’ --skip-grant-tables 上图 错误 注意&#xff1a;如果遇到mysqld: Can’t change dir…

【从零学习python 】02. 开发工具介绍

文章目录 编写Python代码一、常见的代码编辑工具二、运行Python程序三、Pycharm的下载和安装PyCharm的主要功能区域进阶案例 编写Python代码 根据我们之前介绍的知识&#xff0c;我们知道&#xff0c;所谓代码其实就是将一段普通文本按照一定的规范编写&#xff0c;然后交给电…

Cesium 加载ArcGIS Server切片服务错级问题

1.首先上官方api说明 ArcGisMapServerImageryProvider - Cesium Documentation 里面没有 zoomoffset参数!!! 2.如果按照互联网栅格切片规则 3857、4326、4490常用切片层级参数,则直接加载显示地图 viewer.imageryLayers.addImageryProvider(new Cesium.ArcGisMapServerI…

【Spring Boot】(三)深入理解 Spring Boot 日志

文章目录 前言一、日志文件的作用二、Spring Boot 中的日志2.1 查看输出的日志信息2.2 日志格式二、Spring Boot 中的日志2.1 查看输出的日志信息2.2 日志格式 三、自定义日志输出3.1 日志框架3.2 日志对象的获取3.3 使用日志对象打印日志 四、日志级别4.1 日志级别的作用4.2 日…

Oracle-expdp报错ORA-39077、06502(Bug-16928674)

问题: 用户在使用expdp进程导出时&#xff0c;出现队列报错ORA-39077、ORA-06502 ORA-31626: job does not exist ORA-31638: cannot attach to job SYS_EXPORT_SCHEMA_01 for user SYS ORA-06512: at "SYS.DBMS_SYS_ERROR", line 95 ORA-06512: at "SYS.KUPV$…

【修正-高斯拉普拉斯滤波器-用于平滑和去噪】基于修正高斯滤波拉普拉斯地震到达时间自动检测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

解密HTTP代理爬虫中的IP代理选择与管理策略

在当今数据驱动的世界中&#xff0c;HTTP代理爬虫作为一项重要的数据采集工具&#xff0c;其成功与否往往取决于IP代理的选择与管理策略。作为一家专业的HTTP代理产品供应商&#xff0c;我们深知IP代理在数据采集中的重要性。在本文中&#xff0c;我们将分享一些关于HTTP代理爬…

图像膨胀+滤波达到边缘外扩模糊效果

有一个扯淡需求, 根据某些格网值渲染对应的颜色, 我们做的实现方案是按照色代码渐变做颜色映射, 但是某些厂家不顾结果正确性与否, 应是为了好看做的好看, 将边界膨胀模糊, 一个非风场,力场类似场数据做了一个类似场的渲染效果, 也不知道说啥好, 例如原始图渲染如下 经过一系列…

Spring Boot配置文件与日志文件

1. Spring Boot 配置文件 我们知道, 当我们创建一个Spring Boot项目之后, 就已经有了配置文件存在于目录结构中. 1. 配置文件作用 整个项目中所有重要的数据都是在配置文件中配置的&#xff0c;比如: 数据库的连接信息 (包含用户名和密码的设置) ;项目的启动端口;第三方系统的调…

资深测试总结,Web自动化测试POM设计模式封装框架,看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 线性脚本 import…

ParallelCollectionRDD [0] isEmpty at KyuubiSparkUtil.scala:48问题解决

ParallelCollectionRDD [0] isEmpty at KyuubiSparkUtil.scala:48问题解决 这个问题出现在使用Kyubi Spark Util处理ParallelCollectionRDD的过程中&#xff0c;具体是在KyubiSparkUtil.scala文件的第48行调用isEmpty方法时出现的。该问题可能是由以下几个原因引起的&#xff1…

FFmpeg将编码后数据保存成mp4

以下测试代码实现的功能是&#xff1a;持续从内存块中获取原始数据&#xff0c;然后依次进行解码、编码、最后保存成mp4视频文件。 可保存成单个视频文件&#xff0c;也可指定每个视频文件的总帧数&#xff0c;保存多个视频文件。 为了便于查看和修改&#xff0c;这里将可独立的…

[CKA]考试之检查可用节点数量

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 检查集群中有多少节点为Ready状态&#xff08;不包括被打上 Taint&#xff1…

FPGA学习——Altera IP核调用之PLL篇

文章目录 一、IP核1.1 IP核简介1.2 FPGA中IP核的分类1.3 IP核的缺陷 二、PLL简介2.1 什么是PLL2.2 PLL结构图2.3 C4开发板上PLL的位置 三、IP核调用步骤四、编写测试代码五、总结 一、IP核 1.1 IP核简介 IP核&#xff08;知识产权核&#xff09;&#xff0c;是在集成电路的可…

视频监控汇聚平台EasyCVR视频分享页面WebRTC流地址播放不了是什么原因?

开源EasyDarwin视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多…