开发采纳答案的功能
回顾达内知道业务流程
开发显示学生问题详情的页面
首先明确讲师问题详情页和学生问题详情页的区别
我们可以将讲师的问题详情页复制,起名为学生的问题详情页
具体操作
1.删除现有的detail_student.html
2.复制detail_teacher.html
3.粘贴为detail_student.html
4.删除detail_student.html中不应该有的内容
删除的内容自己参考项目,或按上图中的示意删除即可
下面要想学生登录到学生首页,然后通过标题连接
跳转到学生详情页的话,需要在index_student.html页面的标题链接处修改href属性
index_student.html的210行附近
<a class="text-dark" href="question/detail.html"
v-text="question.title"
:href="'/question/detail_student.html?'+question.id">
eclipse 如何导入项目?
</a>
重启服务测试,学生登录首页后点击标题链接
采纳答案的业务思路
我们的课程中,只完成学生采纳答案的功能
但是一个完善的问答系统,答案的采纳功能应该至少包含下面的3中情况
- 学生采纳答案:学生认为自己的问题已经得到解决,在学生的问题详情页中,选择满意的答案,通过点击"采纳答案"按钮完成采纳业务
- 讲师采纳答案:如果经过了一定的时间,对已经回答的问题,提问的学生一直没有新的反馈,讲师是可以代替学生点击"采纳答案"按钮完成采纳业务的
- 系统自动采纳:当一个问题长时间没有任何新的变化,我们可以编写程序定时的按事先设计好的规则进行采纳工作
下面来介绍一下学生采纳回答的业务流程
- 采纳答案也是不可逆操作,需要二次确认
- 业务逻辑层要判断正在执行采纳的学生是问题的提问者
- 采纳答案业务涉及两个表的修改操作
- answer表的accept_status列修改值为1
- question表的status列修改值为2
达内知道项目的采纳答案后业务的约束
本项目不限制一个问题只能采纳一个回答
本项目不限制一个已经标记为解决的问题继续添加新的回答和评论
采纳答案的二次确认效果
采纳答案的二次确认和删除的基本一致
只是删除的红× 变成采纳的绿√
detail_student.html的368行附近
<a class="btn btn-primary mx-2 text-white"
style="cursor: pointer"
onclick="$(this).next().toggle(300)"
>采纳答案</a>
<a class="badge badge-pill badge-success text-white"
style="display: none;cursor: pointer"
@click="answerSolved(answer.id)">
<i class="fa fa-check"></i>
</a>
重启服务,测试采纳答案的二次确认效果
编写js采纳代码
上面的二次确认按钮调用了answerSolved方法
现在js代码中还没有编写这个方法,我们现在先新增这个方法
question_detail.js文件红,answersApp对象中添加方法
answerSolved:function(answerId){
axios({
url:"/v1/answers/"+answerId+"/solved",
method:"get"
}).then(function(response){
alert(response.data);
})
}
编写采纳答案的数据访问层
上面业务流程分析中,我们确定了这个业务要修改answer和question两张表
这两个修改写在不同的Mapper接口中
AnswerMapper接口编写修改answer表accept_status的方法
/*
java编译器编译程序时,底层在默认情况下,是不会保存局部变量名称的
因为方法的参数也是局部变量,所以方法的参数名称在编译之后也会消失
因为消失,就不能对应sql语句中使用#{}定义的变量名了
导致Mybatis(Plus)框架在有多个参数时,必须使用其它注解来对应变量名
但是SpringBoot官方脚手架对java编译器的参数进行了修改,使得java编译器在编译后
也能保存局部变量的名称,所以当前项目是能够自动匹配变量名称和#{}中的名称的
但是使用其它公司或组织提供的SpringBoot脚手架(例如阿里)创建的SpringBoot项目
就没有对java编译器参数进行修改,以至于运行下面的代码会因为变量名不能对应到#{}中
的名称而报错
所以最终为了保证程序能够正常运行
我们一般都会添加一个@Param注解,来标识对应#{}中的名称,保证万无一失
*/
@Update("update answer set accept_status=#{acceptStatus} " +
" where id=#{answerId}")
int updateAcceptStatus(@Param("acceptStatus") Integer acceptStatus,
@Param("answerId") Integer answerId);
下面要转到QuestionMapper添加修改问题状态的方法
// 修改问题状态的方法
@Update("update question set status=#{status} " +
" where id=#{questionId}")
int updateStatus(@Param("status") Integer status,
@Param("questionId") Integer questionId);
定义Question类中的状态常量
我们先确定Question类status列3个取值的含义
一般情况下,凡是数据库表中的列表示状态值,而且状态是3个以及3个以上时,一般都会在对应的实体类中定义常量来表示其含义
Question实体类中添加以下常量
public class Question implements Serializable {
private static final long serialVersionUID = 1L;
// 3个状态常量
public static final Integer POSTED=0; // 已提交\未回复
public static final Integer SOLVING=1; // 正在采纳\已回复
public static final Integer SOLVED=2; // 已采纳\已解决
// 以下代码略
}
编写业务逻辑层代码
IAnswerService 接口中添加采纳答案的业务逻辑层方法
// 根据回答的id采纳答案的业务逻辑层方法
boolean accept(Integer answerId,String username);
AnswerServiceImpl实现类代码如下
@Autowired
private QuestionMapper questionMapper;
@Override
// 本方法要连续进行answer表和question表的两次修改,必须添加事务的支持
@Transactional
public boolean accept(Integer answerId, String username) {
User user=userMapper.findUserByUsername(username);
// 根据answerId查询出Answer对象
Answer answer=answerMapper.selectById(answerId);
// 根据answer对象的questId获得Question对象
Question question=questionMapper
.selectById(answer.getQuestId());
// 判断当前登录用户是不是问题的发布者
if(user.getId().equals(question.getUserId())){
// 身份判断无误,开始进行修改
int num = answerMapper
.updateAcceptStatus(1,answerId);
if(num!=1){
throw new ServiceException("数据库异常");
}
// 修改完answer,开始修改question
num=questionMapper.updateStatus(
Question.SOLVED,question.getId());
if(num!=1){
throw new ServiceException("数据库异常");
}
// 返回true,表示修改一切顺利
return true;
}
// 程序运行到此处,表示没有进入上面的if
// 也就是当前登录用户不是问题的发布者,返回false表示这个情况
return false;
}
使用人数比较少,所以对网站的性能要求并不是非常高
但是,一般情况下,企业级应用的业务流程复杂度比较高,权限管理也比较复杂
2.互联网应用
一般指对全国乃至全世界开放的网站或服务器程序
京东,淘宝,饿了么,美团,高德,抖音,qq音乐,爱奇艺,微博等
它们对网站性能要求时非常严格的
并发访问量非常大时,还需要能够非常迅速的给出响应
对性能的要求可以归纳为:高并发,高可用,高性能
一般情况下,互联网项目业务比较简单,但是对性能要求高
Java项目分类小结
上面章节我们了解了企业级应用和互联网应用的特征
那么开发项目时使用的技术可以基本确定为
- 企业级应用使用单体项目结构的情况比较多
- 互联网应用使用微服务项目结构的情况比较多
微服务概述
什么是微服务
微服务的概念是由Martin Fowler(马丁·福勒)在2014年提出的
微服务是由以单一应用程序构成的小服务,自己拥有自己的行程与轻量化处理,服务依业务功能设计,以全自动的方式部署,与其他服务使用 HTTP API 通信。同时服务会使用最小的规模的集中管理能力,服务可以用不同的编程语言与数据库等组件实现。
简单来说,微服务就是将一个大型项目的各个业务拆分成多个互不干扰的小项目,而这些小项目专心完成自己的功能,而且可以调用别的微服务的方法,从而实现最终的整体功能
京东\淘宝等大型互联网应用程序,基本每个业务的每个流程都是一个微服务项目在支持:
- 搜索服务器
- 登录服务器
- 商品信息服务器
- 购物车服务器
- 支付服务器
- 物流服务器
- …
为什么需要微服务
单体项目虽然不能支持高并发,高可用,高性能
但是服务器数量少,成本低,适合性能要求不高的项目
微服务项目虽然能够支持高并发,高可用,高性能
但是服务器数量比较多,成本高,适合性能要求高,不惜成本的项目
单体项目虽热不能支持高并发,高可用,高性能
但是服务器数量少,成本低,适合性能要求不高的项目
微服务项目虽然能够支持高并发,高可用,高性能
但是服务器数量比较多,成本高,适合性能要求高,不惜成本的项目
怎么搭建微服务项目
在微服务概念提出之前(2014年之前),每个厂商都有自己的一套解决方案
但是Martin Fowler(马丁·福勒)提出了微服务的标准之后,为了技术的统一和兼容性,很多企业开始支持微服务标准,开发微服务的项目结构的程序
我们要想搭建支持马丁·福勒标准的微服务程序,自己编写是不现实的
我们需要学习能够快速搭建微服务项目的框架
一般情况下我们会使用SpringCloud作为实现手段
什么是SpringCloud
SpringCloud是由Spring提供的一套能够快速搭建微服务项目架构的框架集
SpringCloud本身不是一个框架,是一系列框架或软件的统称
SpringCloud中的内容都是为了搭建微服务项目而出现的
有人将SpringCloud称之为"Spring全家桶",广义上指Spring提供的所有框架
SpringCloud包含的内容
内容的提供者角度来说
- Spring自己编写的框架和软件
- Netflix:奈非(早期提供了很多微服务组件)
- alibaba:阿里巴巴(现在越来越多的在使用)
从功能上分类来说:
- 微服务的注册中心
- 微服务的网关
- 微服务间的调用
- 微服务配置管理
- 微服务的安全和权限管理
- …
Nacos注册中心
什么是Nacos
Nacos是Spring Cloud Alibaba提供的一个软件
这个软件可以将当前项目所有微服务项目的信息进行注册,收集和管理
也就是注册中心
Nacos是一个使用java开发的软件,我们启动Nacos其实就是启动了一个java项目,这个项目启动后,允许我们编写的微服务项目进行注册
下面我们就来学习如何安装和启动nacos
Nacos是java开发的,所以需要当前计算机支持java的环境变量
nacos下载路径
https://github.com/alibaba/nacos/releases/download/1.4.2/nacos-server-1.4.2.zip
国外网站,下载较慢多试几次
java环境变量的配置
windows系统环境变量配置过程如下:
1."计算机\此电脑"图标上单击右键->属性
点击高级系统设置
点击环境变量
检查是不是包含JAVA_HOME的配置,并且配置的路径确实包含jdk文件夹
如果没有配置需要点击新建按钮,按上图配置即可
没有安装java的同学先安装java再配置
安装启动Nacos
我们下载了nacos软件
将压缩包解压(注意,路径中尽量不要包含中文和空格)
解压得到的文件夹中,有一个bin文件夹,打开之后有如下内容
startup.cmd是windows启动nacos的命令
shutdown.cmd是windows停止nacos的命令
.sh结尾的文件是linux和mac系统的启动和停止文件
启动Nacos并不是双击启动文件
需要我们打开dos窗口来执行
Win+R然后运行cmd
启动命令中standalone表示本次启动是单机模式运行
如果不指定会以默认的集群模式运行,会无法完成效果
startup.cmd -m standalone
如果启动成功显示8848端口
就以进行下面的访问了
localhost:8848/nacos
如果访问这个路径长时间没有响应,可以将dos窗口关闭之后再重新启动尝试(获得直接重启电脑)
登录系统
用户名和密码默认都是nacos
到此为止 nacos的安装和启动就完成了
我们注册的演示使用knows-resource项目
也就是之前创建的静态资源服务器项目
将它启动时注册到Nacos中
注册到Nacos的操作,并不复杂,我们需要记住配置的过程就可以了
因为现在是第一次在SpringCloud环境下进行配置
我们需要将父项目的pom文件进行比较多的修改,让父项目支持微服务类型的子项目
配置内容较多,直接发给大家
父项目pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>knows</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>knows</name>
<description>Demo project for Spring Boot</description>
<packaging>pom</packaging>
<modules>
<module>knows-resource</module>
</modules>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
<mybatis.plus.version>3.3.1</mybatis.plus.version>
<pagehelper.starter.version>1.3.0</pagehelper.starter.version>
<knows.commons.version>0.0.1-SNAPSHOT</knows.commons.version>
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
<spring-security-jwt.version>1.0.10.RELEASE</spring-security-jwt.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>knows-commons</artifactId>
<version>${knows.commons.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</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>${spring-security-jwt.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
下面转到knows-resource项目去完成下面的配置过程
步骤1:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringCloudAlibaba 注册到Nacos的依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
步骤2:
knows-resource的application.properties文件
# 定义当前微服务项目的名称,当前项目会使用这个名称注册到nacos
spring.application.name=resource-server
# 定义正在运行的Nacos的位置,其实就是ip地址和端口号
# 其实这个配置的默认值就是localhost:8848,当今情况是可以不配置的
# 但是实际开发中注册中心在默认位置的几率很低,所以一定会配置
spring.cloud.nacos.discovery.server-addr=localhost:8848
步骤3:
最后在knows-resource项目的SpringBoot启动类中添加注解
表示这个项目启动时要注册到注册中心
@SpringBootApplication
// 下面的注解表示项目启动时,会注册到注册中心,成为一个微服务项目
@EnableDiscoveryClient // EDC
public class KnowsResourceApplication {
public static void main(String[] args) {
SpringApplication.run(KnowsResourceApplication.class, args);
}
}
测试注册是否能够完成
1.先必须保证Nacos正在执行,nacos的dos界面不能关闭,一关闭就停止了
2.再运行knows-resource项目
3.在nacos界面中的服务管理->服务列表中,看到我们设定的微服务名称:resource-server既表示成功!