# 在线教育
**Repository Path**: eeecho7/online-education
## Basic Information
- **Project Name**: 在线教育
- **Description**: No description available
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-01-01
- **Last Updated**: 2022-10-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 谷粒学院 - 项目搭建
> 新的一年,步步向前。
## Day01
### 1.Mybatis-Plus
> Mybatis-Plus版本:3.3.1
>
> Spring Boot版本:2.2.1.RELEASE
#### 1.1 概述
- 润物无声
- 只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 效率至上
- 只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间
- 丰富功能
- 热加载、代码生成、分页、性能分析等功能一应俱全
#### 1.2 测试
1. 创建数据库和相应的表
2. 编写Java代码
1. 导入依赖
2. 创建实体类
3. 创建mapper接口,继承Mybatis-Plus的接口
3. 编写测试类
> 注意:使用容器技术一定要先进行类的注入,不同层的注入注解不同
>
> dao层为:@Repository
### 2.主键策略
#### 2.1 概述
随着业务规模的不断扩大,需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。
数据库的扩展方式主要包括:业务分库、主从复制,数据库分表。
#### 2.2 雪花算法
分表的策略有:水平分表、垂直分表和分布式ID生成器。这里推荐使用第三种方式,并且通过雪花算法实现。
雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的主键的有序性。
核心思想:
- - 长度共64bit(一个long型)。
- 首先是一个符号位,1bit标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0。
- 41bit时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于69.73年。
- 10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID,可以部署在1024个节点)。
- 12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID)。
> 如果需要在Mybatis-Plus中使用相关主键策略,首先需要数据库中相应表主键自增的支持,其次需要在Java代码中添加相应的注解支持。
>
> Mybatis-Plus自动驼峰转换。
>
> 转换成10进制:用19位表示。
#### 2.3 测试
不使用Mybatis的雪花算法
1. 添加主键自增
2. 改变数据库的自增的起始值
3. 测试
### 3.自动填充和乐观锁
#### 3.1 自动填充概述
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。
我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作。
#### 3.2 测试
数据库中增加插入时间和更新时间字段
1. 添加字段
2. Java中对应的实体类,添加属性
3. 添加相关注解,实现业务操作时,自动生成相关时间
4. 实现相关接口方法 - MetaObjectHandler接口
5. 测试
#### 3.3 乐观锁概述
在操作前先查询,如果和想要的不同,修改失败,重新获取,再次修改。
#### 3.4 问题导入
并发更新
1. 创建数据库表并添加数据
2. Java代码...
3. 两个用户分别获取数据
4. 两个用户分别修改数据
5. 查看最后结果
> 结果:价格从开始的100变为70
#### 3.5 问题修改
使用乐观锁,重试并发更新
1. 修改时,添加版本限制
2. 实体类添加注解@Version
3. 创建MybatisPlus配置类,添加乐观锁插件
4. 添加MapperScan注解
5. 在配置类上添加事务支持注解(暂时用不到)
6. 测试,插入失败后,重启发起修改
1. 重新获取新的Version
2. 再次修改
> 价格变为120,实现乐观锁。
### 4.分页查询
> 查看代码:com.pox.mybatisplus.MybatisPlusCURDTest
#### 4.1 测试
分页查询
1. 添加分页查询插件
2. 查询所有字段
3. 查询指定字段
1. 需要查询的字段封装到Map中,避免数据的冗余
### 5.逻辑删除
推荐是逻辑删除,不要直接将数据删除。
#### 5.1 概述
物理删除和逻辑删除
- 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
- 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
> 在数据库添加相应字段,在Java代码中添加相关注解,实现,以后Java代码中的每次查询都会在where中添加上delete=0(前面添加的字段为delete且设置默认值为0)。
### 6.条件构造器和常用接口
#### 6.1 概述
通过条件构造器,实现更加丰富的增删查改操作。
> 代码查看:com.pox.mybatisplus.QueryWrapperTests
#### 6.2 条件语句含义
| **查询方式** | **说明** |
| ---------------- | --------------------------------- |
| **setSqlSelect** | 设置 SELECT 查询字段 |
| **where** | WHERE 语句,拼接 + WHERE 条件 |
| **and** | AND 语句,拼接 + AND 字段=值 |
| **andNew** | AND 语句,拼接 + AND (字段=值) |
| **or** | OR 语句,拼接 + OR 字段=值 |
| **orNew** | OR 语句,拼接 + OR (字段=值) |
| **eq** | 等于= |
| **allEq** | 基于 map 内容等于= |
| **ne** | 不等于<> |
| **gt** | 大于> |
| **ge** | 大于等于>= |
| **lt** | 小于< |
| **le** | 小于等于<= |
| **like** | 模糊查询 LIKE |
| **notLike** | 模糊查询 NOT LIKE |
| **in** | IN 查询 |
| **notIn** | NOT IN 查询 |
| **isNull** | NULL 值查询 |
| **isNotNull** | IS NOT NULL |
| **groupBy** | 分组 GROUP BY |
| **having** | HAVING 关键词 |
| **orderBy** | 排序 ORDER BY |
| **orderAsc** | ASC 排序 ORDER BY |
| **orderDesc** | DESC 排序 ORDER BY |
| **exists** | EXISTS 条件语句 |
| **notExists** | NOT EXISTS 条件语句 |
| **between** | BETWEEN 条件语句 |
| **notBetween** | NOT BETWEEN 条件语句 |
| **addFilter** | 自由拼接 SQL |
| **last** | 拼接在最后,例如:last(“LIMIT 1”) |
> 01-06代码查看:F:\谷粒学院\code\java\mybatis-plus
### 7.模块的创建
#### 7.1 概述
创建父模块、Common模块、Service模块来做公共模块。在这两个模块下再创建具体的模块来进行业务代码的编写。
#### 7.2 步骤
`注意:`在创建子模块时,一定要选择正确的parent模块。
###### 1.创建Common
1. 在parent模块下创建
2. maven项目
3. 导入dependencies
###### 2.创建common_util
1. 在common模块下创建
2. maven项目
###### 3.创建service
1. 在parent模块下创建
2. maven项目
3. 导入dependencies
###### 4.创建service_edu
1. 在service模块下创建
2. maven项目
### 8.代码生成器
#### 8.1 概述
通过代码生成器来创建多层代码。
#### 8.2 步骤
1. 在测试代码块中创建相应类
2. 编写业务代码
> 生成代码如果没有第一时间出现在Idea中,选择重写加载即可。
3. 数据表中公共属性的抽取
> 将所有表中的公共字段抽取出来。
1. service_base模块中创建公共类
2. 修改代码生成器
3. 删除已经生成的,重新运行代码生成器
### 9.讲师Controller层
#### 9.1 概述
创建讲师Controller层的部分代码并进行测试。
#### 9.2 步骤
###### 1.创建会员管理系统
1. Servic_edu模块的Controller下添加一个新包 - admin
2. 将代码生成器生成的TeacherController移到admin包下
3. 在TeacherController的总@RequestMapping加上:/admin
###### 2.编写TeacherController层业务
1. 注入TeacherService
2. 查询讲师列表
1. 调用service层,写Controller内容
问题:查询出的时间显示和真实时间差距8个小时
解决方案:在总配置文件中添加相关配置。
```yaml
spring:
jackson: #返回json的全局时间格式
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
```
问题:查询出的时间和数据库的时间不一致
解决方案:在需要修改的时间字段上添加相关注解
```java
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
// 添加这个注解,按照相应的格式形式返回
```
3. 删除讲师
1. 编写相应业务代码
2. 使用ApiPost进行对应的Delete测试
> 代码查看:com.pox.guli.service.edu.controller.admin.TeacherController
### 10.Swagger
#### 10.1 概述
前后端分离开发模式中,api文档是最好的沟通方式。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)可测性 (直接在接口文档上进行测试,以方便理解业务)
- 前端工程师编写接口文档(使用swagger2编辑器或其他接口生成工具)
- 交给后端工程师
- 根据swagger文档编写后端接口
- 最终根据生成的swagger文件进行接口联调
#### 10.2 整合Swagger
添加依赖即可。文档上的显示信息,均可以在Java代码上通过注解添加。善用各种Swagger的注解来丰富显示信息。
#### 10.3 接口分组
按照分组在Swagger页面上显示。
1. 在Service_base模块中创建配置文件类
1. 添加@Configuration注解
2. 添加@EnableSwagger2注解
2. 注入相关Bean
3. 根据路径分组
4. 修改配置文件类
5. 创建相关Bean
1. 创建分组名字
2. 添加路径配置
### 11.Controller层的统一返回结果
#### 11.1 步骤
1. 在Common_util中创建统一返回类
2. 创建相关类
1. 枚举类
2. 返回类
#### 11.2 修改Controller层
返回值修改。
> 链式掉用返回,每一个方法返回时的结果为其本身(this),因此可以链式的不断调用下去。
```java
public R success(Boolean success){
this.setSuccess(success);
return this; // 返回本身,链式调用支持
}
```
#### 11.3 丰富TeacherController
###### 1.分页查询
1. 传递传输
1. SQL语句需要的两个参数
2. 根据分页查询语句查询
3. 返回结果
###### 2.修改分页查询,添加可以根据条件查询进行分页
当前端需要多个数据字段时,最好的做法是封装到一个对象中 - vo。
1. 在entity包下创建vo包
2. 创建所需要的封装对象
3. 复杂业务代码,自己创建
1. 编写Service层相应接口
2. 实现方法
业务代码实现
1. 分页查询
2. 条件查询
3. 根据字段排序
4. 分页查询
1. 传入的对象为空,直接分页即可
5. 根据条件查询具体讲师
1. 传入的ov对象,获取其中数据
2. 根据查询出的条件判断,是否添加到条件查询中
6. 组装条件
7. 返回结果
1. 直接使用baseMapper查询
> 因为该类继承了MybatisPlus的基础类,在加上泛型,这里的baseMapper就是Teacher本身
注意:对于一些必须需要传递的参数(不传递就会报错的参数),可以通过Swagger的参数注解添加上具体参数来要求请求传递时,必须要携带这些参数。
###### 3.新增讲师
1. 编写Controller层代码
1. PostMapping
2. 添加相关Swagger注解 - 规范化编程
3. 添加@RequestBody注解
> 支持Json形式传递
2. 调用Service层
3. 返回结果
1. 调用统一返回结果类
###### 4.更新讲师
1. 编写Controller层代码
2. 调用Service层
3. 编写业务代码,返回不同的结果
1. 更新成功
2. 更新失败
> 编写业务代码一定要多加判断,想到成功和不成功等情况。
###### 5.根据Id查询讲师
1. 编写Controller层代码
2. 调用Service层
3. 编写业务代码
1. 获得讲师对象
2. 根据获取对象的内容返回结果
4. 返回对象
问题:传入的对象中有创建时间和修改时间字段,我们传入的时候没有传入,会报错
解决方案:
1. 可以通过在编写业务代码时,添加相关处理方式 - 不推荐
2. 添加相关自动字段填充类 - 推荐
1. 在Service_base模块中添加相关handler
2. 在抽取出的字段类中添加相关注解来进行字段填充
### 12.统一异常处理
#### 12.1 步骤
1. 在Service_base模块中创建异常处理器
1. 添加注解@ControllerAdvice
2. 编写相关方法
1. 添加注解@ExceptionHandler支持异常处理
2. 添加注解@ResponseBody Json支持
3. 编写方法体
#### 12.2 添加具体异常的捕获
1. 对上面的代码进行修改
2. 修改方法体代码
> 优先匹配,找不到会向上寻找。
> 使用其他模块的类,除了要添加相关的依赖以外,还要在主启动类上添加相关的注解扫描,能将其他模块的中的配置类、工具类等,扫描得到。
### 13.统一日志处理
#### 13.1 控制台日志
修改控制台日志显示级别,输出具体的日志内容。
#### 13.2 logback日志
推荐。在需要使用的地方添加配置文件即可。
>代码查看:common、service模块。
## Day02
### 1.前端框架建设
1. 安装Node.js
2. VSCode
3. 使用elementUi框架
#### 1.1 Node.js
> node版本:v10.14.2
>
> npm版本:6.4.1
##### 1.1.1 概述
脱离浏览器环境也可以运行JavaScript,只要有JavaScript引擎就可以。
Node.js是一个基于Chrome V8引擎的JavaScript运行环境:
即Node.js内置了Chrome的V8 引擎,可以在Node.js环境中直接运行JavaScript程序。
- 在Node.js中写JavaScript和在Chrome浏览器中写JavaScript**基本**没有什么不一样。哪里不一样呢?
- - Node.js没有浏览器API,即document,window的等
- 加了许多Node.js 专属API,例如文件系统,进程,http功能
> 丰富了前端的功能与开发。
###### 1.1.2 作用
如果你想开发类似JavaWeb的简单的后端程序,那么学习Node.js是一个非常好的选择。
如果你想部署一些高性能的服务,那么学习Node.js也是一个非常好的选择。
- - 通常他会被用来作一个BFF层,即 Backend For Frontend(服务于前端的后端),通俗的说是一个专门用于为前端业务提供数据的后端程序
> BFF用于数据的裁剪和拼装,当然这部分是可以写在Java代码中的,但是交给Node是更加方便的。
##### 1.1.3 安装
1. 安装Node.js
2. 修改为淘宝镜像:npm config set registry https://registry.npm.taobao.org
**跨域问题:**同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互,所谓同源(即指在同一个域)就是两个地址具有相同的协议(protocol),主机(host)和端口号(port)
**解决方案:**在需要跨域JS的Controller层添加@CrossOrigin注解,解决跨域问题。
#### 1.2 VsCode
1. 安装插件
##### 1.2.1 ESLint插件
ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。
不管是多人合作还是个人项目,代码规范是很重要的。这样做不仅可以很大程度地避免基本语法错误,也保证了代码的可读性。这所谓工欲善其事,必先利其器,推荐 ESLint+vscode 来写 vue,有种飞一般的感觉。
每次保存,vscode就能标红不符合ESLint规则的地方,同时还会做一些简单的自我修正。
> 这里推荐将VSCode自动保存关闭,然后启用相关设置,每一次自己保存,可以看见修正。
#### 1.3 ElementUI
###### 1.3.1 前端结构
```shell
├── build // 构建相关
├── config // 配置相关
├── src // 源代码
│ ├── api // 所有请求
│ ├── assets // 主题 字体等静态资源
│ ├── components // 全局公用组件
│ ├── directive // 全局指令
│ ├── filtres // 全局 filter
│ ├── icons // 项目所有 svg icons
│ ├── lang // 国际化 language
│ ├── mock // 项目mock 模拟数据
│ ├── router // 路由
│ ├── store // 全局 store管理
│ ├── styles // 全局样式
│ ├── utils // 全局公用方法
│ ├── vendor // 公用vendor
│ ├── views // view
│ ├── App.vue // 入口页面
│ ├── main.js // 入口 加载组件 初始化等
│ └── permission.js // 权限管理
├── static // 第三方不打包资源
│ └── Tinymce // 富文本
├── .babelrc // babel-loader 配置
├── .eslintrc.js // eslint 配置项
├── .gitignore // git 忽略项
├── favicon.ico // favicon图标
├── index.html // html模板
└── package.json // package.json
```
##### 1.3.2 运行前端框架
在UI路径下执行npm install,如果不行多次执行。运行成功后,尝试登录,根据自己的需求修改页面。
尝试登录,第一次登录不进去,因为框架中的路径请求问题,需要修改为自己的,并且根据所需的参数等,构建Controller层返回值。
> 代码查看:com.pox.guli.service.edu.controller.UserController - servic_edu模块
### 2.页面开发
#### 2.1 前端页面开发流程
1. 创建路由
2. 创建API - Java中Service层
3. 创建页面组件 - Java中Controller层
#### 2.2 调用后端接口
1. 调用后端接口
2. 编写后端业务代码
3. 返回前端所需数据
### 3.阿里云存储OSS
#### 3.1 开通服务
1. 开通服务
2. 创建Bucket
3. 创建文件夹
4. 创建用户
1. 保存信息,退出就不会显示,只会显示一次
> 创建Bucket的方式有两种,一种是直接在网页端创建,另一种是在Java代码中创建。
>
> 文档参考:https://help.aliyun.com/product/31815.html
### 4.文件上传模块
#### 4.1 步骤
1. 在service模块下创建
2. 添加依赖
1. 父模块中的版本管理将相关依赖切换版本到3.8.0
3. 创建yaml文件
1. 修改yaml内容
4. 添加日志输出
1. 创建logback配置文件
2. 修改日志输出位置
5. 创建主启动
1. @ComponentScan
2. 添加自动装配排除,父模块中添加了MySQL依赖,但是上传服务模块中不需要使用
> 父模块如果引用了相关驱动,那么其子模块也会拥有。
>
> 如:父模块添加了MySQL驱动,虽然子模块用不到相关依赖,但是会因为依赖的问题,spring会去找寻相关的源,如果此时子模块不用也没有配置相应的配置,那么就会报错。
>
> 解决:在主启动类上排除相关的自动配置类,通过注解实现。
>
> `@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)`
#### 4.2 读取配置文件
文件上传模块需要许多配置信息,来上传到对应的服务器,这个配置信息很复杂,我们可以将它保存到配置文件中,需要的时候直接读取配置文件,这里有两种方式,一种是直接建立一个properties文件,另一种是在application配置文件中添加,然后我们读取总配置文件即可,推介后者。
##### 4.2.1 步骤
1. 配置文件中添加相关键值对
2. 创建一个封装类来读取这些文件
> 通过封装类来获取我们所需要的内容
3. 通过相关注解来和配置文件中存储的键值对进行绑定
4. 将该类装配到容器中
```java
@Data // set方法
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class OssProperties {
private String endpoint;
private String keyid;
private String keysecret;
private String bucketname;
}
```
> 值注入,需要相关的set方法,其实也可以通过@Value注解来读取配置文件,但是这样代码量就膨胀了,这样直接和配置文件绑定,更简便,但是要注意配置文件中的值的缩进问题。
#### 4.3 文件上传业务
##### 4.3.1 步骤
1. 编写Controller层
2. 编写Service层
```java
@Override
public String upload(InputStream inputStream, String module, String originalFilename) {
// 通过封装类读取配置文件信息
String endpoint = ossProperties.getEndpoint();
String keyid = ossProperties.getKeyid();
String keysecret = ossProperties.getKeysecret();
String bucketname = ossProperties.getBucketname();
//判断oss实例是否存在:如果不存在则创建,如果存在则获取
OSS ossClient = new OSSClientBuilder().build(endpoint, keyid, keysecret);
if (!ossClient.doesBucketExist(bucketname)) {
//创建bucket
ossClient.createBucket(bucketname);
//设置oss实例的访问权限:公共读
ossClient.setBucketAcl(bucketname, CannedAccessControlList.PublicRead);
}
//构建日期路径如:avatar/2021/02/26/文件名
String folder = new DateTime().toString("yyyy/MM/dd");
//文件名:uuid.扩展名
String fileName = UUID.randomUUID().toString();
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
//路径拼接
String key = module + "/" + folder + "/" + fileName + fileExtension;
//文件上传至阿里云
ossClient.putObject(ossProperties.getBucketname(), key, inputStream);
// 关闭OSSClient。
ossClient.shutdown();
//返回url地址,根据oss服务进行组装
return "https://" + bucketname + "." + endpoint + "/" + key;
}
```
> 对于业务层的代码,可以参考OSS网站上的帮助文档。
>
> 先编写Controller层,在编写Service层。
### 5.自义定异常处理
#### 5.1 概述
自定义异常处理,返回具体的异常给前端和控制台。
#### 5.2 创建自定义处理器
1. service_base模块创建
2. 编写通用异常处理类
1. 继承异常类 - RuntimeException
2. 定义为运行时异常
> 如果直接继承为Exception,Exception是一种检查型异常,它会继续往上抛出异常,我们需要自己来解决异常,所以继承运行时异常,捕获并处理
3. 在全局的异常处理器中添加相关的方法来处理抛出的自定义异常
> 如果没有在全局异常处理器中定义自定义异常的处理方式,那么就会直接走总异常,而不是通过我们自定义的异常来处理
4. 编写异常处理方式
3. 爆出异常的地方捕获异常
1. 抛出自义定的通用异常
> 因为前面定义了全局异常处理器,那么后面我们创建自定义异常处理器,也一定要到全局异常处理器中“注册”,不然,全局异常不知道我们的存在,而是自己处理了。
> 代码查看:service_oss、service_edu、service_base模块。
## Day03
### 1.引入Nacos服务中心
1. 总模块中添加Nacos和Spring Cloud依赖
2. 本地下载Nacos客户端
3. 修改具体服务模块yaml,注册到Nacos
4. 主启动类上添加服务发现注解
> 服务名的修改,不推荐在单词之间用下划线分割,用中划线代替。
### 2.引入OpenFeign远程调用
1. Service模块添加依赖
2. 具体模块的主启动类添加@EnableFeignClients注解
3. 编写远程调用测试案例
> 在服务的调用端添加这个注解,推荐给服务的提供者也添加上这个注解。
>
> OpenFeign通常是接口来实现远程调用,因此没有具体的实现类,通常就会给这个接口上添加注解注入到容器中。
#### 2.1 负载均衡概述
OpenFeign自带Ribbon,为了负载均衡的方便测试,可以使用IDEA中的模块复制,并修改端口号。
#### 2.2 负载均衡策略
对于服务端需要调用生产端,可以为具体的每一个生产端的负载均衡进行设置。
```yaml
# 针对不同的服务的提供方的负载均衡策略,以oss服务端为例
service-oss: # 调用的提供者的名称,这里的名称为具体的提供者
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
```
#### 2.3 负载均衡的策略模式
| 策略名 | 策略描述 |
| ------------------------- | ------------------------------------------------------------ |
| BestAvailableRule | 选择一个最小的并发请求的server |
| AvailabilityFilteringRule | 过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(activeconnections 超过配置的阈值) |
| WeightedResponseTimeRule | 根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。 |
| RetryRule | 对选定的负载均衡策略机上重试机制。 |
| RoundRobinRule | 轮询选择server |
| RandomRule | 随机选择一个server |
| ZoneAvoidanceRule | 综合判断server所在区域的性能和server的可用性选择server |
> 可以通过上面的方式在具体的服务端配置文件中配置。
#### 2.4 OpenFeign超时控制
OpenFeign的远程调用通常是1s,因此可能会房很容易超时,建议在配置文件中修改这个时间。
```yaml
ribbon:
ConnectTimeout: 10000 #连接建立的超时时长,默认1秒,这里设置为10s
ReadTimeout: 10000 #处理请求的超时时间,默认为1秒
```
#### 2.5 OpenFeign重试机制
当OpenFeign远程调用的时候,如果在规定的时间了没有返回结果,就会再次发送一次,重试。
```yaml
ribbon:
MaxAutoRetries: 0 # 同一实例最大重试次数,不包括首次调用,默认0
MaxAutoRetriesNextServer: 1 # 重试其他实例的最大重试次数,不包括首次所选的server,默认1
```
#### 2.6 OpenFeign日志
可以配置相关的配置类开启OpenFeign的日志功能,并在配置中设置相应的日志级别并开启日志。
### 3.OSS文件删除
通过这个OSS文件删除总结一个开发流程。
#### 3.1 步骤
1. 提供方编写业务代码
2. 提供方提供业务调用接口
3. 调用方远程调用
#### 3.2 详细 - 提供方编写业务代码
1. 编写Service层 - FileService 接口
2. 实现接口,编写方法体
3. 编写Controller层 - FileController
4. 编写业务方法
#### 3.3 详细 - 调用方
1. 编写远程调用接口 - OssFeignService
2. 编写Service层 - TeacherServiceImpl
3. 调用远程调用接口
4. 编写Controller层
> 服务的开发,可以从Service出发,也可以从Controller出发。
>
> 修改前端代码。
### 4.Sentinel
#### 4.1 服务雪崩
分布式系统环境下,通常会有很多层的服务调用。由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了问题,调用这个服务就会出现线程阻塞的情况,此时若有大量的请求涌入,就会出现多条线程阻塞等待,进而导致服务瘫痪。
如下图,对于同步调用,当底层的库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。
由于服务与服务之间的依赖性,故障会传播,不可用沿请求调用链向上传递,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的 “雪崩效应” 。
> 因为一个服务的超时可能导致整个系统的瘫痪。
#### 4.2 服务容错
要防止雪崩的扩散,我们就要做好服务的容错:保护自己不被猪队友拖垮的一些措施。
常见的容错方案:隔离、超时、限流、熔断、降级。
##### 4.2.1 隔离
将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体的系统服务。常见的隔离方式有:线程池隔离和信号量隔离。
##### 4.2.2 超时
上游服务调用下游服务时,设置一个最大响应时间,如果超过这个时间,下游未作出响应,就断开请求,释放掉线程。
##### 4.2.3 限流
限制系统的输入和输出流量以达到保护系统的目的。为了保证系统的稳定运行,一旦达到的需要限制的阈值,就采取相应措施以完成限制流量的目的。
##### 4.2.4 熔断
当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
##### 4.2.5 降级
降级就是为服务提供一个托底方案,一旦服务无法正常调用,就使用托底方案。
> 常应用于服务的调用方,提供者出现问题,可以通过服务降级来给用户友好的体验。
#### 4.3 Sentinel 概述
Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
> 新一代服务容错解决方法。
#### 4.4 Sentinel 特性
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 SpringCloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
- 完善的扩展点:Sentinel 提供简单易用、完善的扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
> 比起前人更加优秀,实战经验丰富。
#### 4.5 整合
1. 添加依赖
2. 配置配置中心,能够被Sentinel监控
#### 4.6 Sentinel 重要功能
##### 4.6.1 流量控制
思想:保证自己不被上游服务压垮
任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel可以根据需要把随机的请求调整成合适的形状。
##### 4.6.2 熔断降级
思想:保证自己不被下游服务拖垮
当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。
##### 4.6.3 系统负载保护
思想:保证外界环境良好(CPU、内存)
当系统负载较高的时候,如果还持续让请求进入可能会导致系统崩溃,无法响应。在集群环境下,会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
#### 4.7 OpenFeign 中的服务降级
1. 编写服务降级类
1. 实现具体接口的方法
2. 编写降级规则
2. 在OpenFeign注解中添加服务降级
```java
@Service
public class OssFileServiceFallBack implements OssFileService {
@Override
public R test() {
return R.error();
}
@Override
public R removeFile(String url) {
log.info("熔断保护");
return R.error();
}
}
```
```java
@FeignClient(value = "service-oss", fallback = OssFileServiceFallBack.class)
```
### 5.EasyExcel
#### 5.1 引用场景
1. 数据导入
2. 数据导出
3. 数据传输
> 对于许多需要录入、数据表格导出的场景都有很大的应用场景,对于异构系统之间的数据传输。
#### 5.2 EasyExcel 特点
- Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
- EasyExcel是阿里巴巴开源的一个excel处理框架,**以使用简单、节省内存著称**。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
- EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。
### 6.课程类目管理开发
#### 6.1 Excel 导入
1. 添加EasyExcel依赖
2. 将Excel的每一列封装为一个实体类
3. 创建监听器,实现Excel导入
> 代码查看:service_edu下com.pox.guli.service.edu.listener.EasyExcelListener
#### 6.2 Excel 数据导入到数据库
1. 读取每一行的数据
2. 判断
3. 封装到数据库表的实体类中
> 代码查看:service_edu下com.pox.guli.service.edu.listener.EasyExcelListener
#### 6.3 编写业务代码
1. 编写Service
1. 需要重写编写自关联SQL语句
```xml
```
2. 编写Controller
3. 修改Mapper.xml文件的读取,在配置文件中添加mapper文件的读取
> 注意:只有自动注入到容器中的类,才可以使用主动注入注解。
>
> 监听器没有直接注入到容器中,因此我们需要在创建监听器的地方,传入它所需依赖的类。
## Day04
### 1.添加页面
1. 课程发布
2. 课程列表
3. 富文本编辑器
### 2.业务逻辑编写
1. 复杂SQL语句的编写
2. Mybatis-Plus 条件查询组装查询
### 3.复杂SQL语句查询
1. 分别查询
2. 组装封装对象
3. 返回数据
#### 4.引入阿里云VOD
> 代码查看:ChapterController、CourseController、VideoController
>
> MyBatis-Plus执行完语句后就可以通过get的方式获取存储在数据库中的内容。
>
> 318 - 346
## Day05
### 1.课程视频删除业务
### 2.服务器渲染
#### 2.1 什么是服务端渲染
服务端渲染又称SSR (Server Side Render)是在服务端完成页面的内容渲染,而不是在客户端完成页面内容的渲染。
SSR并不是前端特有的技术,我们学习过的JSP技术和Thymeleaf技术就是典型的SSR
服务端渲染的特点:
- 在服务端生成html网页的dom元素
- 客户端(浏览器)只负责显示dom元素内容
#### 2.2 什么是客户端渲染
客户端(浏览器) 使用AJAX向服务端发起http请 求,获取到了想要的数据,开始渲染html网页,生成dom元素,并最终将网页内容展示给用户。
客户端渲染的特点:
- 在服务端只是给客户端响应的了数据,而不是html网页
- 客户端(浏览器)负责获取服务端的数据生成dom元素
#### 2.3 两种方式各有什么优缺点?
##### **客户端渲染:**
缺点:不利于网站进行SEO,因为网站大量使用javascript技术,不利于搜索引擎抓取网页。
优点:客户端负责渲染,用户体验性好,服务端只提供数据不用关心用户界面的内容,有利于提高服务端的开发效率。
适用场景:对SEO没有要求的系统,比如后台管理类的系统,如电商后台管理,用户管理等。
##### **服务端渲染:**
优点:有利于SEO,网站通过href的url将搜索引擎直接引到服务端,服务端提供优质的网页内容给搜索引擎。
缺点:服务端完成一部分客户端的工作,通常完成一个需求需要修改客户端和服务端的代码,开发效率低,不利于系统的稳定性。
适用场景:对SEO有要求的系统,比如:门户首页、商品详情页面等。
### 3.Nuxt.js
####3.1 Nuxt.js介绍
移动互联网的兴起促进了web前后端分离开发模式的发展,服务端只专注业务,前端只专注用户体验,比如流行的vue.js实现了功能强大的前端渲染。 但是,对于有SEO需求的网页如果使用前端渲染技术去开发就不利于SEO了,有没有一种即使用vue.js 的前端技术也实现服务端渲染的技术呢?
Nuxt.js 是一个基于 Vue.js 的轻量级应用框架,可以用来创建服务端渲染 (SSR) 应用,也可充当静态站点引擎生成静态站点应用,具有优雅的代码结构分层和热加载等特性。
官网网站:https://zh.nuxtjs.org/
####4.2 Nuxt.js服务器端渲染
下图展示了从客户端请求到Nuxt.js进行服务端渲染的整体的工作流程:
1)用户打开浏览器,输入网址请求到Node.js中的前端View组件
2)部署在Node.js的应用Nuxt.js接收浏览器请求,并请求服务端获取数据
3)Nuxt.js获取到数据后进行服务端渲染
4)Nuxt.js将html网页响应给浏览器
> 浏览器器渲染,收到数据以后通过浏览器来进行页面渲染。也叫客户端渲染。
>
> 服务器渲染,在后端的Controller层将页面渲染后,将页面返回给浏览器。
### 4.前台页面开发
前台页面展示开发,编写相关业务层代码。
### 5.视频播放整合
通过阿里云的VOD云业务进行视频播放整合。
### 6.后台CMS整合
CMS内容管理器,通过CMS管理前台的数据显示。
# 谷粒学院 - 学习总结
## 1.项目结构
### 1.1 整体框架
- 父模块
- 公共模块
- 治理模块
- 业务模块
### 1.2 父模块
这次项目是将所有的模块都创建到一个模块下,创建一个父模块可以进行更好的依赖管理,版本仲裁等优势。
> dependencyManagement 是对版本的管理,实际上是并没有引入真实的依赖。通过在父模块中的版本定义,可以对整体项目的版本之间稳定性。
> 注:依赖的版本设定是非常重要的事,各个依赖的正确定义可以减少很多BUG的产生,因此在项目框架选型的时候,一定要对具体依赖的官网上版本支持进行认真的选择。
### 1.3 父模块 - 公共模块
#### 1.3.1 创建方式
在创建模块下的子模块时,一定要选择正确的父模块,否则可能会造成依赖管理错误的情况。
#### 1.3.2 作用
在父模块下创建公共模块,可以将一些模块之间都会引用的一些类创建其中,然后通过依赖引入的方式将公共模块引入,这样可以使项目结构更清晰,并且可以避免代码膨胀。
这里将公共模块分为:
- 公共工具类模块
- 公共业务模块
具体的划分方式根据实际情况而定,可以先从业务模块开始创建,当多个模块有公共使用时,考虑是否将该公共使用抽取出来。
引入的方式:
```xml
com.pox
common_util
0.0.1-SNAPSHOT
```
#### 1.3.3 子模块分类 - 工具类模块
除了一些工具类以外,还可以抽象出项目返回类型,如:R
R 统一返回类,定义了返回类别,根据项目进行抽象定义,定义在工具类中可以便于各个模块之间的直接使用。
#### 1.3.4 子模块分类 - 服务公共模块
将服务业务模块抽取出来,比如,一些共同使用的配置类、异常处理类、处理器、数据交互模型、业务数据传输对象(DTO)等,为整体的业务服务模块服务。
### 1.4 父模块 - 治理模块
#### 1.4.1 作用
可以在治理模块中,创建一些服务治理模块,如:网关模块等,通过这些治理模块进行对项目的管理。
### 1.5 父模块 - 服务模块
#### 1.5.1 依赖引入
具体模块下的依赖管理,将业务模块中需要使用的依赖提取出来,引入依赖,不是像父模块下的依赖管理,而是直接引入依赖。
> 对于每一个层级的父模块对于依赖的管理,可以是设定依赖管理、版本仲裁等,但是对于层级比较低的模块,更好的方式是直接引入依赖。
#### 1.5.2 常用业务依赖
- Nacos
- OpenFeign
- Sentinel 服务容错
- Web-Start
- Mybatis-Plus
- MySQL
- Swagger
- joda-time 日期时间工具
- lombok
- commons-io
- json
- Juit 测试
#### 1.5.3 作用
主要的作用是依赖引入,避免子模块重复的依赖引入,子模块中自己特别需要的依赖,模块再引入。
> 整体模块的依赖引入,不能因小失大,比如:可能有些模块用不到数据库等依赖,不能因为个别模块而在父模块中去除数据库依赖,而是可以在具体模块中,设置排除掉这个依赖。
### 1.6 总结
项目的分级模块,可以将一些共有的属性抽取出来,避免代码膨胀,架构清晰,但是还是要注意高内聚、底耦合,每一个具体的模块,根据实际情况进行添加。
## 2.业务模块
### 2.1 概述
- CMS 内容管理
- EDU 主业务
- OSS 远程文件管理
- STATISTICS 图表管理 数据分析
- TRADE 订单管理
- UCENTER 用户管理
- VOD 视频管理
### 2.1 CMS 内容管理
#### 2.1.1 功能
对前台页面的展示数据的管理,如:广告显示管理、排序管理等。
通过在后台的数据管理,实时的更新前台的数据显示。
#### 2.1.2 业务分析
主要是基础的单表的增删改查。对于一些文件的删除,除了数据的逻辑删除外,还有对远程服务器上文件删除。
#### 2.1.3 业务分析 - OpenFeign 远程调用
1. 创建远程调用接口
- 添加@Service注解,用于注入到容器 (1)
- 添加@FeignClient注解并设定调用服务块名称,如需要服务降级处理,还需要进行指定具体的服务降级类 (2)
- 指定调用路径 (3)
2. 编写业务代码
- 接口方法定义
- 具体实现类
- 注入远程接口
- 调用方法
3. 编写Controller 返回数据
> (1):对于不同层次有不同的注入注解,最基本的组件注入注解为@Component
> (2):@FeignClient(value = "service-oss", fallback = OssFileServiceFallBack.class),这里的服务名,需要配合注册中心,根据注册中心的服务名称调用具体的服务模块,对于具体的服务降级类。创建服务容错类,实现远程调用接口,编写服务容错方法。
```java
// 容器注入
@Service
@Slf4j
public class OssFileServiceFallBack implements OssFileFeignService {
@Override
public R removeFile(String url) {
log.info("熔断保护");
return R.error().message("调用超时");
}
}
```
> (3):对于路径,一定要组装完全,不要忘记Controller类上的路径设置,可以通过Swagger上的路径进行确认。
#### 2.1.4 配置管理
yaml文件的缩进问题一定要配置正确,对于引用的依赖配置设置,要根据实际情况进行设置,有一些依赖的运行一定需要设置配置。如:Nacos、MySQL等,在依赖得到引用的时候一定要关注配置设置。
#### 2.1.5 日志
日志是一个非常实用的辅助,可以通过设置日志输出级别,对运行的模块进行监控,当出现错误时,可以即时的看到,并且设置日志输出级别以后,我们可以只关注我们只需要关注一些特定的错误级别。
> 项目引入的logback进行日志输出。
### 2.2 EDU 主体业务模块
#### 2.2.1 概述
EDU模块用于主体业务,是核心模块,对于在线教育项目中的教师、课程、视频等数据的管理。
#### 2.2.2 业务分析 - 通过章节ID删除对应视频
1. 编写Controller 方法
2. 编写具体业务方法
- 条件组装 (1)
- Service中的basemapper (2)
3. 远程调用删除视频
- 调用的VOD模块
> (1):条件组装:QueryWrapper,对于一些复杂的SQL语句Mybatis-Plus给我们提供了条件组装的方法,可以进行一些复杂的SQL语句的拼装,对于更加复杂的SQL,如:多表联查等,推荐在具体Mapper.xml文件中,编写具体SQL查询语句。
> (2):对于具体Service中的basemapper,指的就是这个Service下的mapper,比如在ChapterServiceImpl中的,basemapper指的就是ChapterMapper,因此可以直接通过this来调用mapper中的方法,原理是,ChapterServiceImpl除了实现了ChapterService,也继承了ServiceImpl,在ServiceImpl中就含有basemapper,通过泛型就可以变为具体的basemapper,其他Service同理。(一切建立在Mybatis-Plus框架上)
#### 2.2.3 业务分析 - 分页课程列表查询
根据vo封装对象进行数据库数据分页查询。这个条件查询,需要多表联查,通过编写mapper.xml中的查询语句进行数据查询返回。
1. 编写Controller方法
2. 编写业务方法
- 条件判断
- 条件查询组装
- mapper数据查询
3. 编写Mapper查询语句
> 对于比较复杂的查询语句,好的做法通过编写mapper中的查询语句进行更加定制化的查询。
>
> 可以先在数据库中执行查询语句,然后再到mapper中编写。
#### 2.2.4 业务分析 - 查询热门教师
在前台展示中显示热门教师模块,为了避免重复的数据库查询,可以通过设置缓存的方式,来减少业务流程。
这里使用Redis来做缓存。
1. 引入Redis依赖 (1)
2. 编写Controller方法
3. 编写Service方法
- 具体方法上添加@Cacheable(value = "index", key = "'selectHotCourse'") (2)
4. 返回结果
> (1):除了引入Redis依赖外,还需要引入一个缓存连接池。
```xml
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
```
> (2):V-K代表了在Redis中存在位置
#### 2.2.5 配置分析
使用了Redis缓存,需要在配置中进行连接配置。
### 2.3 OSS 远程文件管理
#### 2.3.1 概述
使用阿里云平台的OSS平台,进行文件的上传和管理。
#### 2.3.2 yaml与封装类的属性绑定
以前读取配置并赋值。现在可以通过在application.yaml配置文件中设置键值对,在具体的封装类上添加相关的注解读取并赋值。
读取同一缩进下的配置,Spring会帮我们进行赋值,我们不用指定的赋值,因此属性的名称一定要和配置中相同。
对配置文件中的数据读取还可以通过@Value(${xxx})来读取并赋值。
属性赋值时,一定要注意缩进。
```java
@Data // get/set方法
@Component // 注入容器
// 指定配置读取
@ConfigurationProperties(prefix = "aliyun.oss")
public class OssProperties {
private String endpoint;
private String keyid;
private String keysecret;
private String bucketname;
}
```
#### 2.3.3 业务分析 - 文件上传
文件上传时,通过前端表单提交接收文件,并对数据进行处理。
1. 编写Controller层方法
- MultipartFile (1)
- 获取文件流
2. 编写业务代码
- 使用OSS服务,上传文件
> (1):含有多种方式可以将获取到的数据,转换为所需。常用的类方法有:
>
> getOriginalFilename 获取文件原名字
>
> getInputStream 获取文件流
>
> getBytes 获取字节数组
#### 2.3.4 OSS文件上传
使用OSS文件服务,首选需要到阿里云平台申请开通,然后还需要一个AccessKey来进行文件上传的验证。
通过将URL拼接的方式,将文件上传到指定的目录。
#### 2.3.5 Spring启动自动配置的排除
在这个模块中用不到MySQL,但在其直接父模块中引入相关依赖,如果直接启动就会报错。因为我们在配置文件中并没有配置相关的数据库连接配置,但是在Spring启动的时候会帮我们自动读取每一个配置类的配置文件并加载,为避免报错,可以通过排除的方式避免自动配置的加载。
```java
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
```
### 2.4 UCENTER 用户管理
#### 2.4.1 概述
用户的管理、登录、数据查询等功能,除了数据库的增删查改以外,最重要的是单点登录 - token模式,这里通过JWT实现。
#### 2.4.2 JWT
客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。
此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。
当跨域时,也可以将JWT放置于POST请求的数据主体中。
#### 2.4.3 JWT配置
通过对JWT配置,可以利用JWT的特性实现单点登录。这里通过Redis来缓存Session。
> 通过将CookiePath设置为根路径有助于查找和使用。
```java
@Configuration
// 缓存HttpSession
@EnableRedisHttpSession
public class HttpSessionConfig {
// 可选配置
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
// 我们可以将Spring Session默认的Cookie Key从SESSION替换为原生的JSESSIONID
serializer.setCookieName("JSESSIONID");
// CookiePath设置为根路径
serializer.setCookiePath("/");
// 配置了相关地正则表达式,可以达到同父域下的单点登录的效果
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}
}
```
#### 2.4.4 业务分析 - 用户登录
对用户表单的验证,验证成功或发放Token,对于限定的范围内,实现单点登录。
1. 引入JWTUtil工具类到工具模块
2. 封装验证对象 (1)
3. 编写Controller方法
4. 编写Service业务方法
- 数据库查询,返回对象
- 密码验证 (2)
- 特殊验证 (3)
- 发放Token (4)
5. 返回数据,存储Token
> (1):对于Token的生成,并不需要所有的用户属性字段,可以选择一些字段组合生成Token。
> (2):用户密码在存储时,是通过MD5存储的,MD5是不可逆的,因此在读取到具体的用户以后,只能通过将用户输入的密码生成MD5再和数据库中的数据匹配。
> (3):不同的项目有不同的验证方式,可以在数据库添加字段或工具类来进行判断,对用户登录进行额外的验证。
> (4):通过工具类生成Token以后返回给用户并设定有效时间,后续其他页面的验证和Redis中缓存进行比较,主要在前端中进行存储和验证。
#### 2.4.5 业务分析 - Token验证
1. 获取请求 HttpServletRequest
2. 读Request读取"token"
3. 根据读取到"token",生成新的Token返回
### 2.5 VOD 视频管理服务
#### 2.5.1 概述
使用阿里云的VOD服务进行视频的存储和播放。也需要AccessKey进行服务使用验证。
#### 2.5.2 VOD播放器
使用VOD依赖,创建一个工具类进行播放凭证的获取。获取到播放凭证以后进行视频的播放服务。
### 2.6 TRADE 订单管理
#### 2.6.1 概述
订单管理模块,用户订单管理。
### 2.7 STATISTICS 数据统计
#### 2.7.1 概述
对于前台的数据分析,定时任务。
设定每天自动生成前天的数据统计。
#### 2.7.2 事务管理
如果需要事务管理,需要正在具体的业务层方法上添加注解并设定rollback类@Transactional(rollbackFor = Exception.class)。
#### 2.7.3 定时任务
##### 1)Timer
使用jdk的Timer和TimerTask可以实现简单的间隔执行任务,无法实现按日历去调度执行任务
##### 2)ScheduledThreadPool线程池
创建可以延迟或定时执行任务的线程,无法实现按日历去调度执行任务
##### 3)quartz
使用Quartz实现 Quartz 是一个异步任务调度框架,功能丰富,可以实现按日历调度
##### 4)Spring Task
Spring 3.0后提供Spring Task实现任务调度,支持按日历调度,相比Quartz功能稍简单,但是开发基本够用,支持注解编程方式
> 使用Spring Task实现。
步骤:
1. 主启动类上添加注解支持@EnableScheduling
2. 设定定时任务
3. @Scheduled(cron = "0 0 1 * * ?")注解设置执行时间
```java
@Slf4j
@Component
public class ScheduledTask {
@Resource
private DailyService dailyService;
/**
* 每天凌晨1点执行定时任务
*/
@Scheduled(cron = "0 0 1 * * ?") // 注意只支持6位表达式
public void taskGenarateStatisticsData() {
//获取上一天的日期
String day = new DateTime().minusDays(1).toString("yyyy-MM-dd");
dailyService.createStatisticsByDay(day);
log.info("taskGenarateStatisticsData 统计完毕");
}
}
```
## 3.辅助工具
### 3.1 Mybatis-Plus
#### 3.1.1 代码生成器
官方文档:https://baomidou.com/pages/981406/
通过代码生成器可以帮助我们快速的通过MySQL中的数据库表生成Mapper、Entity、Controller等,简化开发。
#### 3.1.1 Mapper和Service
Mybatis-Plus自带的Mapper和Service中的一些方法可以帮助我们快速的进行单表查询。
### 3.2 Swagger
#### 3.2.1 文档注释
对于Controller层进行类和方法、参数等的详细注解,可以快速的帮我们定位问题,因此良好的Swagger注释是一个非常好的习惯。
#### 3.2.2 接口测试
在Swagger-UI上可以进行接口测试,这样在将数据传输到前端之前,可以先进行自测,也可以利用PostMan等工具进行更加详细的接口测试。
### 3.3 VO
#### 3.3.1 概述
对于一些前端查询的表单,可以将这些数据进行封装为一个VO,这样我们可以在数据查询时更好的组装和前端传递的数据。
#### 3.3.2 DTO
DTO和VO的区别在于,DTO是项目中统一传递的一个数据方式,每一个服务模块可以DTO进行交互,一般定义到公共模块中。
公共模块的引入和一般的依赖的引入的方式是一样的。
### 3.4 BeanUtils
#### 3.4.1 概述
Spring给我们提供BeanUtils工具类可以帮助我们更加快速和高效,比如:copyProperties方法,可以快速的将两个Object进行属性比较,然后从源对象赋值给目标对象。
### 3.5 logback
#### 3.5.1 功能
修改日志的输出和日志的监控,设置日志输出级别,当出现错误时,即时的输出到指定位置,这样我们可以不用一直的监控。
### 3.5 EasyExcel
#### 3.5.1 概述
可以帮助我们快速的将Excel表中的行和列转换为具体的封装对象,然后进行数据的传递,过程和数据库表的entity对象类似。
## 4.问题
### 4.1 跨域
#### 4.1.1 概述
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。
> 因此服务模块中有跨域时,需要在具体的Controller层加上@CrossOrigin注解,解决端口跨域问题,但是如果使用了网关以后,就要及时去除这个注解,不然前端无法接收到数据。
#### 4.1.1 问题
测试阶段的跨域注解,在使用网关以后,没有及时的去除。
### 4.2 启动报错
#### 4.2.1 问题
在启动时直接退出,没有任何运行结果
#### 4.2.2 原因
引入了logback,logback配置文件配置错误,导致无法再控制台上显示运行详情
#### 4.2.3 解决
通过try catch捕捉异常,发现是日志配置错误。
```java
try {
SpringApplication.run(ServiceCMSMain8140.class, args);
} catch (Throwable e) {
e.printStackTrace();
}
```
## 思维导图
[思维导图文件](F:\谷粒学院\data\notepad)
# 底部