【开源项目--稻草】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.html
        return 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 required
                    v-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

    @Override
    public 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 {@Autowired
    IUserService 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中代码如下

	// 学生发布问题的控制器方法
    @PostMapping
    public 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() {
            @Override
            public void run() {
                synchronized (masters){
                    masters.clear();
                    masterMap.clear();
                }
            }
        },1000*60*30,1000*60*30);
    }
​
​
​
    @Override
    public 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;
    }@Override
    public Map<String, User> getMasterMap() {
        if(masterMap.isEmpty()){
            getMasters();
        }
        return masterMap;
    }

步骤3:

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

步骤4:

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

业务的步骤大概为

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

代码如下

@Autowired
    QuestionTagMapper questionTagMapper;@Override
    public 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(老师)的关系}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

K8S系列文章 之 容器存储基础 Volume

Volume Volume是容器数据卷。我们经常创建删除一些容器&#xff0c;但有时候需要保留容器中的一些数据&#xff0c;这时候就用到了Volume。它也是容器之间数据共享的技术&#xff0c;可以将容器中产生的数据同步到本地。实际就是把容器中的目录挂载到运行着容器的服务器或个人…

云运维工具

企业通常寻找具有成本效益的方法来优化创收&#xff0c;维护物理基础架构以托管服务器和应用程序以提供服务交付需要巨大的空间和前期资金&#xff0c;最重要的是&#xff0c;物理基础设施会产生额外的运营支出以进行定期维护&#xff0c;这对收入造成了沉重的损失。 云使企业…

设计模式之策略模式(Strategy)

一、概述 定义一系列的算法&#xff0c;把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的类而变化。 二、适用性 1.许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。 2.需要使用一个算法的不同变体。…

Grafana V10 告警推送

最近项目建设完成&#xff0c;一个城域网项目&#xff0c;相关zabbix和grafana展示已经完&#xff0c;想了想&#xff0c;不想天天看平台去盯网络监控平台&#xff0c;索性对告警进行分类调整&#xff0c;增加告警的推送&#xff0c;和相关部门的提醒&#xff0c;其他部门看不懂…

Collections工具类(java)

文章目录 7.1 常用方法 参考操作数组的工具类&#xff1a;Arrays&#xff0c;Collections 是一个操作 Set、List 和 Map 等集合的工具类。 7.1 常用方法 Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作&#xff0c;还提供了对集合对象设置不可变、…

基于Mediapipe的姿势识别并同步到Unity人体模型中

如题&#xff0c;由于是商业项目&#xff0c;无法公开源码&#xff0c;这里主要说一下实现此功能的思路。 人体关节点识别 基于Mediapipe Unity插件进行开发&#xff0c;性能比较低的CPU主机&#xff0c;无法流畅地运行Mediapipe&#xff0c;这个要注意一下。 Mediapipe33个人体…

Microsoft Message Queuing Denial-of-Service Vulnerability

近期官方公布了一个MSMQ的拒绝服务漏洞&#xff0c;可能因为网络安全设备的更新&#xff0c;影响业务&#xff0c;值得大家关注。 漏洞具体描述参见如下&#xff1a; Name: Microsoft Message Queuing Denial-of-Service Vulnerability Description: Microsoft Message Queuing…

AI一键生成短视频

AI一键生成推文短视频 阅读时长&#xff1a;10分钟 本文内容&#xff1a; 结合开源AI&#xff0c;一键生成短视频发布到常见的某音&#xff0c;某手平台&#xff0c;狠狠赚一笔 前置知识&#xff1a; 1.基本的 python 编程知识 2.chatGPT 使用过 3.stable diffution 使用过 成果…

8.3一日总结

1.远程仓库的使用 a.克隆远程仓库 1>.在桌面克隆远程仓库 git clone 仓库名 2>.修改仓库内容 3>添加目录 git add. 4>提交: git commit -m 完成登录功能 5>推送提交远程仓库 : git push origin master -u 6>更改推送:git push(简写形式) 需要先添加,再提交,最…

怎么加密文件夹才更安全?安全文件夹加密软件推荐

文件夹加密可以让其中数据更加安全&#xff0c;但并非所有加密方式都能够提高极高的安全强度。那么&#xff0c;怎么加密文件夹才更安全呢&#xff1f;下面我们就来了解一下那些安全的文件夹加密软件。 文件夹加密超级大师 如果要评选最安全的文件夹加密软件&#xff0c;那么文…

FineBI 人力资源 专题

此处使用FineBI处理人力资源数据&#xff0c;数据来源于HR_database数据文件&#xff0c;将此文件拷贝到安装目录下 然后配置数据库连接 在【公共数据】中新建一个文件夹&#xff0c;并将之前数据库中需要用到的表放入此处&#xff0c;更新数据。显示如下。 这时候首先要建立…

goanno的简单配置-goland配置

手动敲注释太LOW,使用插件一步搞定 goanno 打开goanno的配置 点击之后弹窗如下 配置method /** Title ${function_name} * Description ${todo} * Author zhangguofu ${date} * Param ${params} * Return ${return_types} */相关效果如下 同理配置interface // ${interface…

使用TransBigData快速高效地处理、分析、挖掘出租车GPS数据

01、TransBigData简介 TransBigData是一个为交通时空大数据处理、分析和可视化而开发的Python包。TransBigData为处理常见的交通时空大数据&#xff08;如出租车GPS数据、共享单车数据和公交车GPS数据等&#xff09;提供了快速而简洁的方法。TransBigData为交通时空大数据分析的…

一篇万能英语作文范文怎么写?聪明灵犀英语作文写作工具分享

一篇万能英语作文范文怎么写&#xff1f;英语作文是英语学习中不可或缺的一环&#xff0c;但是对于很多人来说&#xff0c;写作并不是一件容易的事情。本文将分享一些实用的英语作文写作工具&#xff0c;帮助你更好地写作。 1. 明确主题 写作之前&#xff0c;首先需要明确主题…

一文让你了解网络安全和云安全的区别与联系

相信大家对于网络安全和云安全的关系不是很了解&#xff0c;今天小编就和大家来一起聊聊网络安全和云安全的区别与联系&#xff0c;仅供参考哦&#xff01; 网络安全和云安全的区别 1、两者定义不同。网络安全通常指计算机网络的安全&#xff0c;实际上也可以指计算机通信网络…

Go Windows下开发环境配置(图文)

Go Windows下开发环境配置 下载 安装 点击下载的安装包进行安装。安装路径可以选择到自己的目录。 环境变量配置 GOROOT&#xff1a;&#xff08;指定到安装目录下&#xff09; GOPATH&#xff1a;&#xff08;是工作空间&#xff09; path&#xff1a;在安装时已经添加了…

日撸java三百行day81-83

文章目录 说明CNN卷积神经网络1. 什么是CNN&#xff08;CNN基础知识&#xff09;1. 基本概念2.输入层3.卷积层3.1 图像3.2 卷积核3.3 偏置数3.4 滑动窗口步长3.5 特征图个数&#xff08;特征图通道数或深度&#xff09;3.6 边缘填充3.7 卷积过程例子 4. 激活函数5. 池化层6.全连…

bagging集成与boosting集成的区别是什么?

bagging集成与boosting集成的区别 区别一:数据方面 Bagging&#xff1a;对数据进行采样训练; Boosting&#xff1a;根据前一轮学习结果调整数据的重要性。 区别二:投票方面 Bagging&#xff1a;所有学习器平权投票; Boosting&#xff1a;对学习器进行加权投票。 区别三:…

Maven-生命周期及命令

关于本文 ✍写作原因 之前在学校学习的时候&#xff0c;编写代码使用的项目都是单体架构&#xff0c;导入开源框架依赖时只需要在pom.xml里面添加依赖&#xff0c;点一下reload按钮即可解决大部分需求&#xff1b;但是在公司使用了dubbo微服务架构之后发现只知道使用reload不足…

网络安全--原型链污染

目录 1.什么是原型链污染 2.原型链三属性 1&#xff09;prototype 2)constructor 3)__proto__ 4&#xff09;原型链三属性之间关系 3.JavaScript原型链继承 1&#xff09;分析 2&#xff09;总结 3)运行结果 4.原型链污染简单实验 1&#xff09;实验一 2&#xff0…