【开源项目--稻草】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.mzph.cn/news/26614.shtml

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

相关文章

Centos中pip install mysqlclient失败

pip install mysqlclient 错误详情如下 pip install mysqlclient Looking in indexes: http://mirrors.tencentyun.com/pypi/simple Collecting mysqlclient Using cached http://mirrors.tencentyun.com/pypi/packages/de/9c/b176826e8994551ce826404dab97e305a4bb76c8b0a4e0…

【《快速构建AI应用——AWS无服务器AI应用实战》——基于云的解决方案快速完成人工智能项目的指南】

基于云的人工智能服务可以自动完成客户服务、数据分析和财务报告等领域的各种劳动密集型任务。其秘诀在于运用预先构建的工具&#xff0c;例如用于图像分析的Amazon Rekognition或用于自然语言处理的AWS Comprehend。这样&#xff0c;就无须创建昂贵的定制软件系统。 《快速构…

【UE4】多人联机教程(重点笔记)

效果 1. 创建房间、搜索房间功能 2. 根据指定IP和端口加入游戏 步骤 1. 新建一个第三人称角色模板工程 2. 创建一个空白关卡&#xff0c;这里命名为“InitMap” 3. 新建一个控件蓝图&#xff0c;这里命名为“UMG_ConnectMenu” 在关卡蓝图中显示该控件蓝图 打开“UMG_Connec…

全志D1-H (MQ-Pro)驱动 OV5640 摄像头

内核配置 运行 m kernel_menuconfig 勾选下列驱动 Device Drivers ---><*> Multimedia support --->[*] V4L platform devices ---><*> Video Multiplexer[*] SUNXI platform devices ---><*> sunxi video input (camera csi/mipi…

<dependency> idea中为什么这个变黄色

在IDE中&#xff0c;当你的代码出现黄色高亮时&#xff0c;通常表示存在警告或建议的提示。对于Maven的<dependency>标签来说&#xff0c;黄色高亮可能有以下几种原因&#xff1a; 依赖项未找到&#xff1a;黄色高亮可能表示IDE无法找到指定的依赖项。这可能是由于配置错…

第 357 场力扣周赛题解

A 故障键盘 简单模拟 class Solution { public:string finalString(string s) {string res;for (auto c: s)if (c ! i)res.push_back(c);elsereverse(res.begin(), res.end());return res;} };B 判断是否能拆分数组 区间dp&#xff1a;定义 p i , j p_{i,j} pi,j​表示子数组 n…

uniapp echarts 点击失效

这个问题网上搜了一堆&#xff0c;有的让你降版本&#xff0c;有的让你改源码。。。都不太符合预期&#xff0c;目前我的方法可以用最新的echarts。 这个方法就是由npm安装转为CDN&#xff0c;当然你可能会质疑用CDN这样会不稳定&#xff0c;那如果CDN的地址是本地呢&#xff1…

【stream的使用】使用stream.filter过滤List对象

Stream初相识 概括讲&#xff0c;可以将Stream流操作分为3种类型&#xff1a; 创建Stream Stream中间处理 终止Steam 每个Stream管道操作类型都包含若干API方法&#xff0c;先列举下各个API方法的功能介绍。 开始管道 主要负责新建一个Stream流&#xff0c;或者基于现有的数组…

Linux下共享windows 一键搞定

编写脚本 [rootlocalhost ~]# vim dd.sh#!/bin/bash yum -y install samba mkdir -p /home/shar sss dddecho " [share]comment Shared Folderpath /homebrowseable yeswritable yesguest ok yes " > /etc/samba/smb.confchmod x /home/* useradd qqqq s…

2023 电赛 E 题 激光笔识别有误--使用K210/Openmv/树莓派/Jetson nano实现激光笔在黑色区域的目标检测

1. 引言 1.1 激光笔在黑色区域目标检测的背景介绍 在许多应用领域&#xff0c;如机器人导航、智能家居和自动驾驶等&#xff0c;目标检测技术的需求日益增加。本博客将聚焦于使用K210芯片实现激光笔在黑色区域的目标检测。 激光笔在黑色区域目标检测是一个有趣且具有挑战性的…

AssetBundleBrowser导入报错解决方案

第一次导入AssetBundleBrowser遇到报错有 Assets\Scenes\AssetBundles-Browser-master\AssetBundles-Browser-master\Tests\Editor\ABModelTests.cs(13,7): error CS0246: The type or namespace name Boo could not be found (are you missing a using directive or an assem…

web系统测试思路

一、输入框 1、字符型输入框&#xff1a; &#xff08;1&#xff09;字符型输入框&#xff1a;英文全角、英文半角、数字、空或者空格、特殊字符“~&#xff01;#&#xffe5;%……&*&#xff1f;[]{}”特别要注意单引号和&符号。禁止直接输入特殊字符时&#xff0c;…

Python如何执行cmd命令和批处理文件

一、Python如何执行cmd命令 方法:使用os.system(command) 完整代码示例&#xff1a; import osos.system(path)二、Python如何执行批处理文件 1、导入subprocess模块 import subprocess2、创建Popen对象并传入.bat文件 subprocess.Popen(rC:\Test\test1.bat)完整代码示例…

Nginx常用功能

Nginx 介绍 Nginx 是开源、高性能、高可靠的 Web 和反向代理服务器&#xff0c;而且支持热部署&#xff0c;几乎可以做到 7 * 24 小时不间断运行&#xff0c;即使运行几个月也不需要重新启动&#xff0c;还能在不间断服务的情况下对软件版本进行热更新。性能是 Nginx 最重要的…

java 两个数比较大小

多个自然数比较大小 package compare; public class Compare { public static void main(String[] args) { int [] c {3,2,5,8,6,4,9}; //找出最大数 //方法 int max0 ; //定义一个变量 for(int i0;i<c.length;i){ //遍历数组 f…

java中的垃圾收集机制

推荐 1 1 垃圾回收 1.1 java的gc堆中的对象而言&#xff0c;什么时候对象会从待回收状态变为激活状态&#xff08;垃圾变成非垃圾对象&#xff09; 当然可以。首先&#xff0c;为了使用 try-with-resources&#xff0c;您需要一个实现了 AutoCloseable 或 Closeable 接口的…

【项目多人协作的困扰】git-cli 解决 git merge 合并时 lock 文件变化,忘记重新安装依赖的问题

项目多人协作的困扰 相信大家多多少少都遇到过&#xff0c;当主线分支的代码&#xff0c;合入到自己的分支的时候&#xff0c;如果这时候&#xff0c;主线中有一些依赖的更新或者添加或者删除&#xff0c;如果合入之后&#xff0c;没有及时的install的话&#xff0c;项目启动的…

Stable Diffusion - Candy Land (糖果世界) LoRA 提示词配置与效果展示

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132145248 糖果世界 (Candy Land) 是一个充满甜蜜和奇幻的地方&#xff0c;由各种各样的糖果和巧克力构成。在糖果世界&#xff0c;可以看到&…

如何搭建个人的GPT网页服务

写在前面 在创建个人的 GPT网页之前&#xff0c;我登录了 Git 并尝试了一些开源项目&#xff0c;但是没有找到满足我个性化需求的设计。虽然许多收费的 GPT网页提供了一些免费额度&#xff0c;足够我使用&#xff0c;但是公司的安全策略会屏蔽这些网页。因此&#xff0c;我决定…

【力扣】344. 反转字符串 <首尾指针>

【力扣】344. 反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1&#xff1a; 输入&#xff1a;s …