续 开发问题采纳的功能
编写显示页面的控制器
不同身份显示不同的问题详情页
老师:detail_teacher.html
学生:detail.html
我们已经将不需要由学生操作的功能从detail.html页面中删除了
我们找到HomeController中跳转到detail.html的方法
去修改,代码如下
//显示问题详情页面
@GetMapping("/question/detail.html")
public ModelAndView detail(
@AuthenticationPrincipal User user){
if(user.getAuthorities().contains(STUDENT)){
//如果是学生,跳detail.html
return new ModelAndView("question/detail");
}else if(user.getAuthorities().contains(TEACHER)){
//如果是老师,跳detail_teacher.html
return new ModelAndView(
"question/detail_teacher");
}
return null;
}
开发采纳答案功能
步骤1:
完成页面上采纳答案的二次确认效果
在点击采纳答案后,弹出二次确认的按钮
detail.html页面的采纳答案链接修改为:
<a class="btn btn-primary mx-2 text-white"
style="cursor: pointer"
onclick="$(this).next().toggle(200)"
>采纳答案</a>
<a class="text-white badge badge-pill badge-success"
style="display: none; cursor: pointer"
@click="answerSolved(answer.id,answer)"
>
<i class="fa fa-check-square"></i>
</a>
步骤2:
编写js文件
在question_detail.js文件的answersApp中编写方法,代码如下
//问题采纳
answerSolved: function (answerId, answer) {
if (!answerId) {
return;
}
//判断这个问题是否已经被采纳
if (answer.acceptStatus == 1) {
alert("此问题已经被采纳")
return;
}
$.ajax({
url: "/v1/answers/" + answerId + "/solved",
method: "get",
success: function (r) {
console.log(r);
if(r.code==ACCEPTED){
answer.acceptStatus=1;
}else{
alert(r.message);
}
}
});
}
步骤3:
开始编写控制器的接收
AnswerController类中添加方法代码如下
@GetMapping("/{id}/solved")
public R solved(
@PathVariable Integer id){
log.debug("收到参数:{}",id);
return R.accepted("采纳成功!");
}
步骤4:
开发数据访问层和业务逻辑层
首先明确采纳业务的数据库操作
修改answer表中accept_status 列的值为1
修改question表中status列的值为2
一个业务有两个修改操作,需要事务的支持
先来编写数据访问层的代码
AnswerMapper接口中添加修改accept_status列的方法
@Update("update answer set accept_status=#{status}" +
" where id=#{answerId}")
int updateStatus(@Param("answerId") Integer answerId,
@Param("status") Integer acceptStatus);
QuestionMapper中添加修改问题状态的方法
@Update("update question set status=#{status} " +
" where id=#{questionId}")
int updateStatus(@Param("questionId") Integer questionId,
@Param("status") Integer status);
推荐大家去测试一下代码运行的效果
步骤5:
推荐在Question实体类中添加问题状态的常量,以便调用和表示
public class Question implements Serializable {
private static final long serialVersionUID = 1L;
//定义问题状态的常量
public static final Integer POSTED=0; //已添加/未回复
public static final Integer SOLVING=1;//正在采纳/已回复
public static final Integer SOLVED=2; //已经采纳/已解决
//..其它代码略
}
步骤6:
编写业务逻辑层
IAnswerService接口中添加方法
//采纳答案的方法
boolean accept(Integer answerId);
AnswerServiceImpl实现类中方法的代码如下
@Resource
private QuestionMapper questionMapper;
@Override
@Transactional
public boolean accept(Integer answerId) {
//查询当前要采纳的answer对象
Answer answer=answerMapper.selectById(answerId);
//判断这个answer是不是已经被采纳
if(answer.getAcceptStatus()==1){
//如果已经被采纳返回false
return false;
}
//开始执行采纳业务
answer.setAcceptStatus(1);
int num=answerMapper.updateStatus(answerId
,answer.getAcceptStatus());
if(num!=1){
throw ServiceException.busy();
}
//修改问题状态为已解决
num=questionMapper.updateStatus(answer.getQuestId(),
Question.SOLVED);
if(num!=1){
throw ServiceException.busy();
}
return true;
}
推荐测试一下
步骤7:重构控制层代码
@GetMapping("/{id}/solved")
public R solved(
@PathVariable Integer id){
log.debug("收到参数:{}",id);
boolean accepted=answerService.accept(id);
if(accepted) {
return R.accepted("采纳成功!");
}else{
return R.notFound("不能重复采纳答案");
}
}
微服务项目结构准备
将现在项目中的pom.xml文件进行重构
重构后的结构为:如图所示
这样的结构方便后续微服务项目搭建,减少pom.xml文件中依赖的冗余
微服务
什么是微服务
2014年马丁.福勒(Martin Fowler),提出了微服务的概念
每个项目应该有自己最小的行程,和轻量化处理
项目与项目之间
使用Http协议通信,可以实现不同编程语言和不同数据库的互通
为什么需要微服务
下面通过一个生活中的案例,来了解微服务的特性
微服务的好处
1.添加和维护服务器方便,对项目整体影响最小化
2.某单一业务的繁忙,不会影响其他业务
3.更适合要求高并发,高可用,高性能的互联网项目使用
微服务结构
如图
什么是SpringCloud
我们自己实现微服务的系统搭建是非常麻烦的
只能使用框架提供的现成的结构
常见的微服务的框架有SpringCloud\Dubbo
我们学习SpringCloud,它解决的主要问题有
-
API Gateway 网关
-
服务间调用
-
服务的发现
-
服务部署
-
服务容错
-
数据调用
SpringCloud提供了一整套微服务的解决方法,大大的降低了项目使用微服务架构的门槛
同时降低了微服务的开发成本
SpringCloud不是一个特定框架,而是一系列程序框架或组件的组合
使微服务项目开发起来非常简单
Eureka 注册中心概述
Eureka就是SpringCloud提供的注册中心
相当于一个家酒店的前台
酒店中可以入住或已经入住的房价信息,都会在前台保存,并可以查询到
当有新顾客前来访问时,可以迅速登记入住
在计算机的世界里变为下图
上面的图中有两个概念
除了注册中心之外还有
服务消费者和服务提供者的概念
两个微服务互相调用,谁主动调用谁是服务的消费者,谁被调用谁是服务的提供者
而注册中心提供了服务列表,列表上有各个服务器能提供的服务,当有服务消费者出现时
可以通过这个列表快速找到合适的服务
创建Eureka项目
再创建一个子项目
这个子项目就是Eureka注册中心
将新创建的子项目中pom.xml文件中的dependencyManagement标签中的内容
和properties中的内容都复制到父项目中
最终的父项目的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.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>straw</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>straw</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<mybatis.plus.version>3.3.1</mybatis.plus.version>
<pagehelper.starter.version>1.3.0</pagehelper.starter.version>
<straw.commons.version>0.0.1-SNAPSHOT</straw.commons.version>
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
</properties>
<packaging>pom</packaging>
<modules>
<module>straw-portal</module>
<module>straw-generator</module>
<module>straw-resource</module>
<module>straw-kafka</module>
<module>straw-commons</module>
<module>straw-search</module>
<module>straw-eureka</module>
</modules>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>straw-commons</artifactId>
<version>${straw.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-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>
</dependencies>
</dependencyManagement>
</project>
最终的子项目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>cn.tedu</groupId>
<artifactId>straw</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>straw-eureka-center</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>straw-eureka-center</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-server
</artifactId>
</dependency>
</dependencies>
</project>
全部复制完毕后进行刷新操作
配置Eureka的启动信息
application.properties
# Eureka服务器端的默认端口是8761,所以,推荐将当前项目的端口设置为8761,则其它各Eureka客户端可以简化配置
server.port=8761
# 每个Eureka Server其实也是一个Eureka Client,默认情况下,也会自行注册
# 以下配置表示“不要注册自己”
eureka.client.register-with-eureka=false
# 由于以上配置了“不要注册自己”,就更“不要抓取注册表”
eureka.client.fetch-registry=false
在SpringBoot的运行类中,添加注解
@EnableEurekaServer代码如下
@SpringBootApplication
@EnableEurekaServer
public class StrawEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(StrawEurekaApplication.class, args);
}
}
然后就可以运行这个main方法然后访问
localhost:8761来验证eureka的运行状态
如果能看到页面,证明运行正常
注册EurekaClient
我们现成有个子项目就是straw-resource它可以充当Eureka的客户端进行运行测试
在straw-resource的pom.xml文件中添加客户端的依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
配置完xml文件之后
每个微服务都有自己的名称
要配置当前微服务的名字
也是配置application.properties添加如下内容
#设置微服务的名字
spring.application.name=resource-server
#如果Eureka的端口不是8761,则下面的配置必须编写
#如果是8761,可以省略
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
如果Eureka的端口是8761可以跳过上面的步骤
在main方法的类上添加注解
@EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
public class StrawResourceApplication {
public static void main(String[] args) {
SpringApplication.run(StrawResourceApplication.class, args);
}
}
Eureka常见名称解释
注册:(registry)
EurekaClient会想EurekaServer提交注册信息
包括实例名,主机地址,端口号
抓取:(fetch)
EurekaServer会整理出一个注册表,注册表中包含所有当前可用的服务器信息
每个EurekaClient在注册之后会立即抓取这个注册表,并缓存到本地
心跳:
心跳是一个周期操作
默认每30秒会向EurekaServer发出续订信息
表示"我"还好好活着
Eureka 集群
所谓集群其实就是多台服务器做同样的事情
当我们的项目需要多个Eureka服务器才能保证运行时
我们就可以搭建Eureka 集群来解决
Eureka 集群的本质就是多个EurekaServer的微服务项目
搭建步骤
-
每个EurekaServer要有不同的端口号
-
编写配置:application.properties文件中
1号项目的配置
server.port=8761 spring.application.name=eureka1 eureka.client.service-url.defaultZone=http://localhost:8762/eureka, http://localhost:8763/eureka eureka.client.register-with-eureka=true eureka.client.fetch-registry=true
2号项目的配置
server.port=8762 spring.application.name=eureka2 eureka.client.service-url.defaultZone=http://localhost:8761/eureka, http://localhost:8763/eureka eureka.client.register-with-eureka=true eureka.client.fetch-registry=true
3号项目的配置
server.port=8763 spring.application.name=eureka3 eureka.client.service-url.defaultZone=http://localhost:8761/eureka, http://localhost:8762/eureka eureka.client.register-with-eureka=true eureka.client.fetch-registry=true
Eureka自我保护机制
当出现图片中的红色文字时表示开启了自我保护机制
英文大意为:
注意!Eureka可能错误将某些已经下线的实例声明为在线,为了安全起见,由于续订比例尚且低于阈值,因此这些实例并不会声明为过期.
出现自我保护机制的原理是EurekaServer在运行期间统计"丢失心跳比例"
如果出现任何连续3次心跳续订失败EurekaClient,都会被认为是异常终止
15分钟内,超过85%的EurekaClient都死了,就会开启自我保护机制
Zuul网关
什么是网关
就是一个项目对外提供的一个统一入口
因为每个服务器都有不同的ip地址或端口号
但是用户访问网站时,确是通过同一个地址访问的,这个地址实际上就是网关
为什么需要网关
因为统一的入口能够方便用户访问,同时隐藏服务器真是的ip和端口号,也有助于安全
使用SpringCloud提供的Zuul组件实现网关项目
它的功能主要包括
- 动态路由
- 洞察于监控
- 身份验证与安全控制
- 其它…
创建一个网关项目
创建一个子项目straw-gateway
没有其它注意事项,注意依赖的勾选即可
修改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>cn.tedu</groupId> <artifactId>straw</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.tedu</groupId> <artifactId>straw-gateway</artifactId> <version>0.0.1-SNAPSHOT</version> <name>straw-gateway</name> <description>Demo project for Spring Boot</description> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> </project>
application.properties
server.port=9000 #给当前实例起名 spring.application.name=gateway #设置动态路由routes.后面的名字是自己起的 #path指定一个路径,在有请求访问这个路径时开始路由 zuul.routes.resource.path=/resource/** #routes.跟的名字和上面的配置必须一致 #service-id跟的必须是已经存在的微服务实例名 zuul.routes.resource.service-id=resource-server
主方法类上添加注解
package cn.tedu.straw.gateway;
@SpringBootApplication
@EnableZuulProxy
public class StrawGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(StrawGatewayApplication.class, args);
}
}
随笔
什么是敏捷软件开发
传统开发模式是先准备,再行动
敏捷开发是发现问题解决问题,出现什么需求就解决什么需求
敏捷开发有准备时间短,实现效率高等优点,
但是由于开发思想就是不断迭代完善,所以可能会频繁的维护和修改代码,很费心力
什么是互联网项目
互联网项目顾名思义就是任何人都可以通过internet访问的项目
一般项目指一个公司内部使用的局域网系统