学习谷粒学苑实录
记:
这是我看谷粒学苑时遇到的问题以及学习的笔记。
前言:
- 记得我看谷粒学苑的时候,每当我遇到问题(bug),在网上搜了半天,大多遇到的问题所处的环境都大不相同,照着文章的方法不一定100%解决,最坏的情况是:bug越改越大。
- 所以 我总会想什么时候会有一篇文章 就像一学长一样,能快速指出是哪块出的问题。这就是我写这篇文章的初衷,也许它很烂,也许也并没有解决你的问题,但总的来说感谢你的观看!!
文章目录
2021年12月15日10:03:49
忘了:
-
@MapperScan("com.liu.eduService.mapper")
// 扫描 Mapper 文件
-
@EnableTransactionManagement
// 开启事务注解,等同于配置文件<tx:annotation-driven/>
2021年12月15日10:50:31
@DeleteMapping
记录@DeleteMapping注解的使用 - 大日很忧伤 - 博客园 (cnblogs.com)
Spring Boot @DeleteMapping - 边见众生,边见自己 - 博客园 (cnblogs.com)
在公共模块配置swagger: 为了让所有模块都能使用
又报错:
焯 !!!
刷一下Maven,发现swagger的配置类变成灰色了!!
- 发现是spring没有扫描到Swagger2配置类,在启动类上加上
@ComponentScan(“com.example.config”)
注解启动再次访问就可以了
swagger-ui.html弹窗报错:Unable to infer base url. This is common when using dynamic…_青山-CSDN博客
2021年12月20日15:45:04
- java将构造方法私有化:
- 可以使得该类不被实例化,和不能被继承。要创建这个类的实例,唯一的办法是提供一个公共静态方法。这种方式被称为单例模式。
class Singleton { // 定义一个类
private static final Singleton INSTANCE = new Singleton() ;
private Singleton() {} // 构造方法私有化
public void print() {
System.out.println("Hello World .") ;
}
public static Singleton getInstance()
{ return INSTANCE ; }
}
-
@Accessors(chain = true)
链式编程 -
@RequestBody
表示当前接收来自前端的JSON数据此时须为
@PostMapping
因为前端不能使用GET方式提交数据,而是用POST方式进行提交
而 POST请求中content的值必须为json格式
@RequestBody和@RequestParam的请求方式get和post关系_qfikh的博客-CSDN博客_@requestparam post请求
这个gmt_modified
还真就没人注意…
三目运算符
return save?R.ok():R.error();
return teacherService.save(eduTeacher)?R.ok():R.error();
- 循环依赖:
面试必杀技,讲一讲Spring中的循环依赖 - 程序员DMZ - 博客园 (cnblogs.com)
2021年12月21日17:06:05
-
// 表明 :该类是注解 式的 异常处理类 @ControllerAdvice
-
日志的等级:
日志记录器(Logger)的行为是分等级的。如下表所示:
优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
# 设置日志级别
logging.level.root=WARN
设置为WARN 则会打印 级别>=WARN的日志
- 默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。
(1条消息) 8个日志级别(OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL)_初飞-CSDN博客_日志级别off
2021年12月23日11:47:37
vue学习
一、基本语法
ES标准中不包含 DOM 和 BOM的定义,只涵盖基本数据类型、关键字、语句、运算符、内建对象、内建函数等通用语法。
本部分只学习前端开发中ES6的最少必要知识,方便后面项目开发中对代码的理解。
1、let声明变量
// var 声明的变量没有局部作用域
// let 声明的变量 有局部作用域
{
var a = 0
let b = 1
}
console.log(a) // 0
console.log(b) // ReferenceError: b is not defined
// var 可以声明多次
// let 只能声明一次
var m = 1
var m = 2
let n = 3
let n = 4
console.log(m) // 2
console.log(n) // Identifier 'n' has already been declared
2、const声明常量(只读变量)
创建 const.html
1、声明之后不允许改变
const PI = "3.1415926"
PI = 3 // TypeError: Assignment to constant variable.
2、常量一但声明必须初始化,否则会报错
// 2、一但声明必须初始化,否则会报错
const MY_AGE // SyntaxError: Missing initializer in const declaration
3、 数组结构:
let q = 1, w=2,e=3
console.log(q,w,e)
var [x,y,z] = [1,2,3]
console.log(x,y,z)
4、声明对象简写:
var a = 1;
var b = 2;
var p1 = {name:a,age:b} // 传统
var p2 = {a,b} // ES6
console.log(p1)
console.log(p2)
es6之扩展运算符 (…) 简称三个点 - 知乎 (zhihu.com)
/**
* 扩展运算符; 。。。取出对象所有可遍历属性然后拷贝到当前对象。
* @type {{name: string, age: number}}
*/
var userr = {
name:"荷兰",
age:16
}
user1 ={...userr}
console.log(user1)
let 方法名 = 参数 => 返回值
var fun2 = (a,b) => a*b
console.log(fun2(2,3))
单向绑定:
v-bind
可简写:一个冒号(:)
使用
v-bind
将title
(html属性) 里的值 与 模型数据绑定:“message”
<div id="app">
<h2 v-bind:title="message">
{{msg}}
</h2>
</div>
<script>
new Vue({
el:'#app',
data:{
msg:"hello,vue",
content: '我是标题',
message: '页面加载于 ' + new Date().toLocaleString()
}
})
</script>
双向绑定
v-model
标明初始值,且输入和应用状态之间的双向绑定。
<div id="app">
<input type="text" v-model="content">
{{content}}
</input>
</div>
组件:
全局组件:
Vue.component(
//组件的名字
"bar",
{ // 组件的内容
template:"<ul> <li>首页</li>" +
"<li>学员管理</li>" +
"<li>讲师管理</li>" +
"</ul>"
})
使用:
<div id="app">
<bar></bar>
</div>
局部组件:
<script>
new Vue({
el:'#app',
data:{
msg:"hello,vue",
},
methods:{
search1:function (){
console.log("aaa")
},
text(){
console.log("bbb")
}
},
components: {
"nar":{
template:"<li>abc</li>"
}
}
})
</script>
axios
axios.提交方式.("请求路径").then(箭头函数).catch(箭头函数)
<script>
new Vue({
el:'#app',
data:{ // 定义变量及其初始值
itemss:[] // 定义接收来自后端的变量
},
//在页面渲染之前执行
created() {
// 调用定义的方法(methods)
this.getlists()
},
methods:{ // 编写具体的方法
getlists(){
/**
* 使用axios发送Ajax请求
* axios.提交方式.("请求路径").then(箭头函数).catch(箭头函数)
*/
axios.post("teacher.json")
.then( response =>{
// response 就是请求之后返回的数据
console.log(response)
this.itemss = response.data.data.lists
}) // 成功的操作
.catch( error =>{
console.log(error)
}) // 失败的操作
}
},
})
</script>
显示其数据:使用 v-for
<div id="app">
<table border="2">
<tr v-for="item in itemss">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.intro}}</td>
<td>{{item.career}}</td>
<td>{{item.level}}</td>
<td>{{item.avatar}}</td>
<td>{{item.sort}}</td>
<td>{{item.isDeleted}}</td>
<td>{{item.gmtCreate}}</td>
<td>{{item.gmtModified}}</td>
</tr>
</table>
</div>
node.js
- 定义: node.js 是JavaScript的运行环境;
- 即:不需要浏览器、服务器也可运行 JS代码
NPM
- node package Manager : Node.js 包的管理工具;
- 相当于前端的Maven,管理前端JS依赖
【已解决】出错的兄弟可以先删除这个文件C:\Users{当前登录的用户名}.npmrc
babel
将es6 代码转为 ES5
(低版本浏览器不支持ES6 [JS标准])
前端模块化
类似于 类与类之间的调用
JS与JS之间的调用
模块化规范:
- CommonJS模块化规范
- ES6模块化规范
Webpack
一个前端资源打包工具
Webpack 可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求。
vue模板:
下载前端依赖: npm install
遇到的错误:
启动vue项目失败,报错Failed at the node-sass@4.14.1 postinstall script. - xiaodangshan - 博客园 (cnblogs.com)
再次使用命令
1、框架的结构:
-
前端入口:
index.html
src 下面:
main.js
-
build目录:
脚本文件
-
config目录:
配置: 项目端口等
dev env.js
: 修改访问后端接口地址
- src:
2、 跨域问题:
从一个域名网页去请求另一域名资源时存在:
协议、域名、 端口任一不同,都是跨域。
协议:http、https
域名:http://www.666.baidu.com *http://www.123.com*
端口: http://www.baidu.com:8080/index.html–> http://www.baidu.com:8081/test.js
解决方式:
- 注解:
@CrossOrigin
- 网关
3、请求机制
第一次:
请求方式:options
:预请求(判断请求是否访问成功),不会返回数据
第二次:发起请求(带数据)
4、开发过程:
- 写路由
router
- 写api:
接收后端的controller方法
- view写页面
接下来的这一段视频,我说一下我遇到的问题
1、加了注解还是遇到了跨域问题,原因时url里面纯在空格导致url错误
2、出现运行时异常,可能是data写错了,或者是没有用老师说的第二种方式
3、能够访问数据,但是控制台报错,原因时config两个地址只改了一个,另一个没改
表单指明为postMapping
,为什么却进入了get方法
Spring MVC 表单提交方式明明已经指明为post,为什么后台却进入了get方法里?-CSDN社区
报错:
使用的是post方法啊,接口里 使用@PostMapping注解,报错:Request method 'GET' not supported
原因: 跨域问题: 加上 @CrossOrign
提前加上了登录验证,登录却是去生产服务器的单点验证的,然后再跳转回来,这样method就被篡改了。果断注销登录验证,提交方式回归正常。
直接在浏览器的URL输入 默认是get!
、
注意:
由于添加与修改是同一页面:区分:
http://localhost:9528/#/edu/teacher/save 为添加页面
http://localhost:9528/#/edu/teacher/edit/1475400900379455490 为修改页面
所以,路径带有id
的为修改页面的 需要做id
查询
created() {
if (this.$route.params && this.$route.params.id) {
// 如果路由(路径)存在且有id
const id = this.$route.params.id
this.findByid(id)
}
},
按钮也是同一个:
<el-button :disabled="saveBtnDisabled"
type="primary"
@click="saveOrUpdate">
保存</el-button>
区分:
saveOrUpdate() {
// 判断 teacher是否有id
if (this.teacher.id){
this.updateteahcer(this.teacher.id)
}else {
this.saveteacher()
}
},
问题:
你们有没有遇到 点修改 后在点 添加 ,表单数据没清空怎么处理
初始化时,判断路径是否有id:
有,则回显数据
没有,则清空表单
init(){
if (this.$route.params && this.$route.params.id) {
// 如果路由(路径)存在且有id
const id = this.$route.params.id
this.findByid(id)
}else {
// 路径没有id,为添加,则清空表单
this.teacher = {}
}
},
注意: created()
方法仅在页面初始加载一次,所以:
watch
来监听。
created() {
this.init()
},
watch: {
$route(to, from) { // 监听
this.init() // 当路由发生变化时调用
}
},
文件上传OOS
- 上传到OOS的文件(若存在文件名重复),则覆盖:
故设置用户上传的文件名字后 加上 UUID
Nginx
Nginx可以做什么?看完这篇你就懂了_Ly-CSDN博客_nginx可以做什么、
Nginx 相关介绍(Nginx是什么?能干嘛?) - 失恋的蔷薇 - 博客园 (cnblogs.com)
正向代理和反向代理的区别:
-
正向代理:
- 类似我们想要访问国外的Google服务器,但是由于访问限制,我们需要找一个代理去访问。换句话说,客户端明确知道要访问的服务器的地址,客户端把请求发送给代理,代理转发给服务器,服务器把响应传给代理,最后代理把响应传给客户端。我们可以看到客户端知道服务器是谁,但是服务器并不知道客户端是谁,这就是正向代理,隐藏了客户端的真实信息。
-
反向代理:
- 类似我们访问淘宝,由于访问量巨大,淘宝会使用许多台服务器(就是分布式服务器)来支持,但是每个客户端的请求到底由哪一台服务器来响应,我们需要一个代理来决定。换句话说,客户端并不知道要把请求发送给哪一台服务器,但是知道发送给哪一个代理,然后代理依据规则(响应时间,负载均衡等)决定把请求转发给哪一台服务器。可以看到,客户端并不知道他访问的服务器是谁,这就是反向代理,隐藏了服务器的真实信息。
-
反向代理服务器
-
请求转发、负载均衡、动静分离
-
请求转发:
Nginx接收到用户/浏览器的请求;根据 转发到不同的服务器上
-
负载均衡:
将多个请求分给不同的服务器去处理
-
动静分离:
将静态资源(图片、配置文件等) 与 动态资源(java 代码等) 分离
配置
报网络错误的可以去看看你的修改配置文件的分号,最好是粘贴上面已经注释语句里面的分号,搞死我了
1、前端记得重启
2、tomcat两个服务记得打开
3、nginx配置文件记得保存
4、niginx关闭再打开
相关命令:
start nginx.exe
nginx.exe -s stop //快速停止nginx
nginx.exe -s reload //重新加载nginx
nginx.exe -s quit //完整停止nginx
nginx -t #检查查看配置文件路径,其配置是否正确
nginx -s reload # 重启
nginx -s quit #退出
ningx -s stop #停止
前端field
等于必须对应后端MultipartFileqi
起的名字
思考
每次上传后都直接存入oss里了,应该要在保存后在统一存入oss,不然会有很多冗余的数据
不可能在点保存的时候才存到oss,因为你要把头像的网址保存到数据库,必须先获得网址,而网址是存入oss,它自动生成的网址。所以必须先存入oss,才能点保存
Nginx再也关过
课程分类
子查询、 连接查询都比单张表查询费时间的多,很多时候都是为了性能牺牲空间和范式
采用文件Excel 上传
2022年1月6日13:11:35:
请注意:这里经过我的测试,不需要在controller里传入subjectService,在new SubjectExcelListener参数传入this即可
给EduSubject的创建时间加上默认的 @TableField(fill = FieldFill.INSERT)
没数据的debug一下看是不是进入监听类了,是不是保存数据的时候edeServiceImpl里面那个new的有参构造没传值
报错的 以后ID自动生成那 如果是String类型 Type那一定要加上STR
@TableId(value = "id", type = IdType.ID_WORKER_STR) // 这个每个实体类都得改动
出现无法读取文件数据:
解决方法:
springboot集成easyExcel写入读取数据为空的问题 - 少侠砍人不用刀 - 博客园 (cnblogs.com)
easyexcel操作文件读取中某列为null_温馨提示 的博客-CSDN博客_easyexcel 读取为null
找不到xlsx文件的改这个: accept=“application/.xlsx”
好坑啊,表单上传的名字和我后端接口的名字不一样。结果收不到文件
2022年1月7日15:04:09
vue 的api 什么时候写 data:
export default {
getDoctor() {
return request({
url: `/medicine/doctor/getAllDoctor`,
method: 'get'
})
},
insertDoctor(doctor) {
return request({
url: '/medicine/doctor/insertDoctor',
method: 'post',
data: doctor
})
},
}
当 前后端需要传递数据(对象等,id除外),且method为post时:
比如:增加,修改,getByType
课程
由于课程的信息的分3步骤进行操作,故一直用实体类过于繁琐:
1、细节问题:
- 创建vo实体类以保存表单的数据
- 将分步表单 提交的数据添加到数据库
- 两张表: 课程表和课程description表
- 将讲师与 课程分类使用下拉列表显示
- 课程分类做成二级联动
bigdecimal是比float、double更高的精度
跳转不了的是因为源码方法名不是next自己改一下
检查没拼写错误的老哥注意methods里的方法next()写在previous()方法的上边
在写入课程基本信息后再 跳转 添加章节 在跳转添加课程的图片、
-
执行全局异常可以把数据库中表description的字段的数据类型从text改为mediumtext或者longtext
-
字段:
subject_parent_id
数据库为空·vo类没有这个字段,加上就行了
2、查询课程里面的章节和小节
- 在Info页面回显数据:
从chapter页面点击上一步:
返回时带有课程id,
从路由获取courseID
,之后查询数据
bug1:
二级分类无法显示,
原因:在数据回显时,二级分类的列表:twoSubjectList
为空、
如下代码 仅在添加页面: 选中一级分类时 调用
// 选中一级分类 回显一级分类的id 事件
OneSubjectChanged: function (value) {
// value 就是被选中的一级分类 id
// console.log(value)
for (let i = 0; i < this.oneSubjectList.length; i++) {
// 判断全部的一级分类的id与 value
var onesubject = this.oneSubjectList[i];
if (onesubject.id === value) {
this.twoSubjectList = onesubject.children;
// this.
//将二级分类的id清空
this.courseInfo.subjectId = ''
}
}
},
回显时twoSubjectList
依旧为空
故 赋值其遍历的二级分类的list
将遍历的一级分类 与 当前回显的一级分类id对比
getCourseInfo: function () { // 数据回显 的 调用
course.getCourse(this.courseId)
.then(res => {
this.courseInfo = res.data.CourseInfoVo;
// 开始填写twoSubjectList
subject.getSubject()
.then(res => {
// 1\ 获取到所有的一级分类
this.oneSubjectList = res.data.list
for (let i = 0; i < this.oneSubjectList.length; i++) {
// 2\ 获取到某个一级分类
var oneSubject = this.oneSubjectList[i];
if (oneSubject.id ===this.courseInfo.subjectParentId ){
// 如果遍历的一级分类id 与 当前回显的一级分类id一致,
// 则赋值其遍历的二级分类的list
this.twoSubjectList = oneSubject.children;
}
}
})
.catch(error => {
console.log(error)
})
})
.catch(error => {
console.log(error)
})
},
编辑按钮点不了 将样式.chanpterList p里面的float: left; 注释掉就可以点了
chapter p 样式加 position: relative;,act 样式加 position: relative;z-index: 1;
3、章节的管理
报错:Invalid bound statement (not found): com..service.mapper...
mybatis-plus 报错:Invalid bound statement (not found): com..service.mapper...***_激情小马哥的博客-CSDN博客
4、课程的最终发布
修改课程的状态status
为“Normal
”
5、课程删除
先删除视频、小节、再删除章节、再删除课程描述、课程本身
bug1:
Missing URI template variable 'courseId' for method parameter of type String
@DeleteMapping
与 @PathVariable
的内容要一致!
6、阿里云:视频点播
域名: 现在2022年1月13日 播放、上传不加密视频 无需域名
api:通过传参数即可调用
API:数据服务功能接口。
SDk:对API进行封装、更便于使用
httpclient: 可替代浏览器对请求进行响应、发布
-
为什么数据库存视频的是id而不是视频地址URL:
- 原因: 上传视频可以进行加密,而加密之后仅通过URL不可播放视频;
- 根据 视频的id可以 获取 凭证+URL 进而播放视频
- 原因: 上传视频可以进行加密,而加密之后仅通过URL不可播放视频;
- 上传视频
- 播放视频
- 删除视频
- jar包问题
配置环境变量时maven文件夹和子文件夹apache-maven-3.6.3之间不要有其他目录,否则环境变量不好使
mvn install:install-file -DgroupId=com.aliyun -DartifactId=aliyun-sdk-vod-upload -Dversion=1.4.11 Dpackaging=jar -Dfile=aliyun-java-vod-upload-1.4.11.jar
- bug2:
implements InitializingBean
的类,在方法之外传值为空
-
视频上传跨域问题
- 没有加注解,
- nginx没有配置8003,
- nginx需要配置最大文件大小,网上有具体操作
client_max_body_size 1024m;
6.1 删除视频
-
删除完课程,如果不保存直接退出,会有bug(阿里云的删除了,但数据库的仍在)
-
删除视频后,需要将视频的id和name清空
-
想要做回显视频的,在编辑小节的方法里获取到小节的集合之后,再加上一句
this.fileList = [{'name': this.video.videoOriginalName}]
7、微服务
微服务就是一个项目可拆分成多个独立的,占用独立进程的服务
Spring Cloud相关基础服务组件
服务发现——Netflix Eureka (Nacos)
服务调用——Netflix Feign
熔断器——Netflix Hystrix
服务网关——Spring Cloud GateWay
分布式配置——Spring Cloud Config (Nacos)
消息总线 —— Spring Cloud Bus (Nacos)
7.1 注册中心
实现不同模块之间的调用,需要将这些模块注册到注册中心中,即可实现互相调用
Nacos
网址:http://localhost:8848/nacos
三步走:
- 添加依赖
- 配置文件加入配置
- 注解
我自己下载的版本启动失败,需要编辑启动文件,set mode="cluster"改为set mode=“standalone”,意思就是集群改为单机
注册不了的在Nacos服务注册依赖中排除guava依赖
Feign
注意 Feign
的控制层:
-
前端调用时; 在Spring MVC中自动将HTTP表单与后端接口参数做了映射(名称一样就能映射成功),这些注解会以参数名称作为默认值,
-
如果是后端RPC调用(比如FeignClient),如果参数前什么也不写,那么会被默认是
@RequestBody
的。则会因为找不到参数而报错,所以,还是得写参数名称。
@PathVariable("videoId")
public interface VodClient {
@DeleteMapping(value = "/eduvod/vod/video/{videoId}")
public R removeVideo(@PathVariable("videoId") String videoId);
}
- 前提条件:服务在注册中心注册
删除视频时,先获取到视频的id,之后删除小节;再删除视频
先删数据库,这个就不用考虑分布式事务问题
这里按照老师写删小节会报错,要用
StringUtils.isEmpty(eduVideo.getVideoSourceId())
调试代码时出现异常:java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path ‘/app/userInfoMaint/getProvince.do’;
发现原来是后台有两个“app/userInfoMaint/getProvince.do”,并且它们路径相同。
p158
@RequestBody
与@RequestParam
@RequestParam什么时候可以省略? - 程序新视界 (choupangxia.com)
什么时候需要@RequestBody注解_FARO_Z的博客-CSDN博客_什么时候用requestbody
- 什么时候需要
@RequestBody
注解:- 传递的是对象,使用
JSON
接收
- 传递的是对象,使用
- 什么时候需要
@RequestParam
注解:-
传递的是普通参数(其实对象也可以,不推荐,需要
JSON
与java对象进行转换) -
该参数需要与前端
/
URL参数 的名字一致,也可以指定参数名:
-
可以设置是否必须传该参数
- 不传递报错400的那种
-
String.join(“,”,videoIdList);
直接就转了
兄弟,你应该加个过滤 .filter(video -> !StringUtils.isEmpty(video.getVideoSourceId()))
filter(x->!StringUtils.isEmpty(x))
.map(EduVideo::getVideoSourceId)
.collect(Collectors.toList())
stream().map(EduVideo::getVideoSourceId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
- bug
真的是,新建视频,上传视频需要等id传来后再添加
上传视频时,不要点太快,视频的id和名字还没有写入数据库就点添加,数据库的id,name就为空
Hystrix
所谓的熔断指的是当服务出现异常或者响应时间过长,将会终止调用,返回一个默认结果
- 在FeignProviderClient定义出通过添加’@feignclient’ 的fallback属性设置映射 降级处理
启动不了项目的 先把设置hystrix超时时间给注释掉
feign.client.config.default.read-timeout=4000 feign.client.config.default.connect-timeout=3000
微软商店安装的ubuntu挺不错的
前台(用户界面)
seo不使用Ajax(异步)的原因:
AJAX、同步、异步对SEO的影响_马恩光的博客-CSDN博客
seo:。搜索引擎采用易于被搜索引用的手段,对网站进行有针对性的优化,提高网站在搜索引擎中的自然排名,吸引更多的用户访问网站,提高网站的访问量,提高网站的销售能力和宣传能力,从而提升网站的品牌效应
SEM:的目的就是让关键词排名靠前,与文本质量以及网站的质量等均无关,而取决于关键词排名位置的,就是您为了这个关键词出价多少。
nux配置文件 最后那个导出部分那段代码,if内部,多加一个配置,options: {fix: true } 就不会报错了
使用这个命令降低swiper的版本,npm install swiper swiper@3.4.2 --save-dev
引入NUXT vue-awesome-swiper 插件出错 Cannot resolve “swiper/dist/css/swiper.css“_OY-CSDN博客
Cannot resolve “swiper/dist/css/swiper.css“_江~仔的博客-CSDN博客
Vue整合swiper报错Could not compile template …swiper\dist\css\swiper.css解决办法 - 等不到的口琴 - 博客园 (cnblogs.com)
nuxt路由
- 固定路由: URL路径是固定的,不发生变化。
to="/course"
是固定的
<router-link to="/course" tag="li" active-class="current">
<a>课程</a>
</router-link>
-
动态路由:
-
每次生成路由地址不一样: 课程详情页传递的id不一样;
-
NUXT的动态路由:下划线开头的vue文件,参数名为下划线后边的文件名
如:在pages下的course目录下创建
_id.vue
-
-
enableDefaultTyping(...) 建议改成 activateDefaultTyping(om.getPolymorphicTypeValidator(), ...)
Springboot整合Redis
CPU最快,一个时钟周期是0.3纳秒,
内存访问需要120纳秒,
固态硬盘访问需要50-150微秒,
传统硬盘访问需要1-10毫秒,
网络访问最慢,都是几十毫秒。
Redis:
将一些数据放到计算机的内存中,且周期性将更新的数据写入磁盘(数据持久化)
- 不同的是,
Memcache
只能将数据缓存到内存中,无法自动定期写入硬盘,- 这就表示,一断电或重启,内存清空,数据丢失。
- 所以
Memcache
的应用场景适用于缓存无需持久化的数据。 - 而Redis不同的是它会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,实现数据的持久化。
- 一般,将经常查询;不常修改的数据放到Redis 中作为缓存
Redis的特点:
1,Redis读取的速度是110000次/s,写的速度是81000次/s;
2,原子 。Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
3,支持多种数据结构:string(字符串);list(列表);hash(哈希),set(集合);zset(有序集合)
4,持久化,集群部署
5,支持过期时间,支持事务,消息订阅
NoSQL: 解决性能问题
2022年1月26日22:54:55
明天看P175
redis报错:
ClassNotFoundException...redis/connection/lettuce/LettuceClientConfiguration$LettuceClientConf
- 原因是 redis的部分依赖写在了
common
;
service-cms
、service-edu
没有导入 Redis依赖
都导入:即可
<!-- 引入 common依赖 2022年1月28日18:42-->
<dependency>
<groupId>com.TestGuLi</groupId>
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
单点登录
Single Sign On,简称SSO
单点登录(SSO)看这一篇还不够~ 这次不慌了_m0_64420350的博客-CSDN博客_sso单点登录
- 定义:在多系统应用群中,在任意系统登录,只需要登录一次,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分
单点登录:
三种实现方式:
第一种: session广播机制:
- session复制:
- 将一个系统的session复制到多个系统内
第二种: 使用cookie + redis 实现
第三种: Token(令牌)
token
:服务端按一定规则生成的一串字符串;(可携带用户信息)
- 作用:
- 以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
JWT
一种用于认证用户信息
- 构成:
- 头部(header):声明 类型jwt和 加密算法(通常直接使用 HMAC SHA256)
- 载荷(playload)(中部)包含需要传递的数据
- 签证 (signature)
- header (base64后的)
- payload (base64后的)
- secret
- 需要base64加密后的header和base64加密后的payload使用
.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密
- 需要base64加密后的header和base64加密后的payload使用
secret是保存在服务器端的,jwt的签发生成也是在服务器端的
所以 secret是私钥
亲测. 首页->云市场->心选市场->搜索短信服务->随便找个买家购买. 卖家商品页面有示列代码,完全可以用。我已经成功了。
申请不通过完全可以跳过发送短信步骤,再后台打印验证,手动再输入就行了
调用jar包:
版本冲突
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.15</version>
</dependency>
<dependency>
<!-- httpclient 版本要与之前的一致-->
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<!-- 报错 NoClassDefFoundError: org/apache/http/config/Lookup 的解决方式
使用 4.3.3 版本-->
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.3.3</version>
</dependency>
<!-- 其余的是GitHub上的 -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>9.3.7.v20160115</version>
</dependency>
linux系统重启后,Redis的数据消失:
linux系统重启后,Redis的数据消失:
Linux Redis 重启数据丢失解决方案,Linux重启后Redis数据丢失解决方 - 艺术攻城狮 - 博客园 (cnblogs.com)
- 发现Springboot连接redis仅可连接6379端口;原因是我的虚拟机只开放了6379端口号;而 Redis服务器地址
spring自带.equalsIgnoreCase(DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)))
Springboot启动scan问题
解决 No qualifying bean of type 问题_sh_c1314-CSDN博客
@SpringBootApplication
//@EnableSwagger2
@ComponentScan(basePackages = {"com.liu"})
@EnableDiscoveryClient
@EnableFeignClients // 使用者调用之
/*
@MapperScan("com.liu.cmsService.mapper")
该注解可以写在此处也可写在MybatisPlusConfig配置类写
* */
public class EduApplication {
public static void main(String[] args) {
SpringApplication.run(EduApplication.class,args);
}
}
@MapperScan和@ComponentScan的区别
-
@ComponentScan
是组件扫描注解,用来扫描@Controller @Service @Repository诸类,主要就是定义扫描的路径从中找出标志了需要装配的类到Spring容器中 -
@Mapper
作用:用在接口类上,在编译之后会生成相应的接口实现类 -
@MapperScan
是扫描指定包下所有的接口类,然后所有接口在编译之后都会生成相应的实现类 -
这两个注解是可以同时使用的。
用户的注册
-
一个用户对应一个手机号
-
为什么先判断验证码在判断手机号.
- 因为不能直接高并发的访问mysql,拿redis进行一次缓冲
报错:java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.lang.String
原因:注入Redistemplate
未指明数据类型
-
接口中参数为
RedisTemplate <String,Object>
取出某 KEY 值时,强制将其转为 String ,出现上述异常
应写成:
@Autowired
private RedisTemplate<String, String> redisTemplate;
copyProperties
BeanUtils.copyProperties
BeanUtils.copyProperties的用法_Smily-王婷婷-CSDN博客_beanutils.copyproperties
-
BeanUtils.copyProperties(“转换前的类”, “转换后的类”);
-
BeanUtils.copyProperties(a, b);
- a的属性包含b的属性 ;
- a中与b中相同的属性都会被替换,不管是否有值;
- a、 b中的属性要名字相同,才能被赋值,不然的话需要手动赋值;
- Spring的BeanUtils的CopyProperties方法需要对应的属性有getter和setter方法;
- 如果存在属性完全相同的内部类,但是不是同一个内部类,即分别属于各自的内部类,则spring会认为属性不同,不会copy;
- spring和apache的copy属性的方法源和目的参数的位置正好相反,所以导包和调用的时候都要注意一下。
Springboot测试类需要写:
@SpringBootTest
@RunWith(SpringRunner.class)
public class CrmBannerControllerTest {
登录注册页面出不来 报错的,先把api创建登陆和注册的js文件 引入进来 不然就报错!!!
:rules="[
{ required: true, // 必须输入值
message: '请输入手机号码', // 提示信息
trigger: 'blur'
}, // 在 失去焦点时触发
{ validator:
checkPhone, // 手机格式校验的方法
trigger: 'blur'
}]"
this.codeTest = 60;放在启动定时器前,解决延迟
用户账号登录:
default.vue
是用写 公共的头部和底部
中间的在page文件里
框架封装的太好,搞得大家都不明白为什么之前用的好好都不需要转,现在却需要转是为什么,大家可以想想jquery是如何处理返回的数据的,jquery需要指定返回的数据类型是json才会自动帮你转化
OAuth2.0
简单来说,jwt的Token 是OAuth 的一种实现方式
try catch快捷键 Ctrl + Alt + T
扫码
String.format() // 创建格式化的字符串以及连接多个字符串对象
%s : 占位符:
String str=null;
str=String.format("Hi,%s:%s.%s", "王南","王力","王张");
System.out.println(str);
Hi,王南:王力.王张
JAVA字符串格式化-String.format()的使用_lonely_fireworks的专栏-CSDN博客_string.format
微信扫码流程
扫描二维码之后,自动跳转至:
http://localhost:8160/api/ucenter/wx/callback?code=091Fs2ml2P9YC84080ll2B2Gbx3Fs2mb&state=testgulili
我现在改成: 害,懒得改@RequestMapping了
http://localhost:8160/UCenterService/ucenter/wx/callback?code=021QlGml2e8aD84yh7ml2Gmf8F3QlGmp&state=testgulili
为了跳转至我们的项目,需要新建一方法:callback()
,
(当然其服务号也得是8160,@RequestMapping("/api/ucenter/wx")
)
参数有: code,state
将已扫描的用户信息也传至首页
-
先由
callback
获取state和code -
在由code 根据微信的固定路径返回
access_token: 访问凭证
、openid: 微信的唯一标识
-
再由第二步的值去获取微信用户的信息:
昵称
、头像
等 -
但由于cookie存在跨域时,无法传递的问题,则:
-
通过URL进行传递:
return "redirect:http://localhost:3000?token=" + token;
-
Java实体类对象,JSON字符串,JSON之间的相互转换_Blogs of imWalker-CSDN博客_java实体类转json字符串
JSON字符串转JSON对象
Gson: JSON字符串获取值
//解析json字符串
Gson gson = new Gson();
HashMap map = gson.fromJson(result, HashMap.class);
String accessToken = (String)map.get("access_token");
String openid = (String)map.get("openid");
fastJSON :
JSONObject jsonObject = JSON.parseObject(AccessTokenUrl);
String accessToken = (String) jsonObject.get("access_token");
String openid = (String) jsonObject.get("openid");
-
前端提交登录信息-》后端将id和昵称封装成token返回给前端-》前端收到token并存入cookie,然后跳转到首页
-
首页判断cookie是否存在token,
- 存在则将token封装到请求体中发送请求获取用户详情信息-》首页显示用户的昵称头像等信息
- 存在cookie中是因为有拦截器会将cookie中的token存到header中,后端也是从hader中取token的,cookie相当于转存的介质
nickname乱码new String(userinfo.getBytes("ISO-8859-1"), "UTF-8");
Vue获取URL参数方式
- Rest风格 获取参数
this.$route.params.id
- 传统获取 (带?)
this.$route.query.token
路由跳转:
this.$router.push({ path: '/order/'+ response.data.data.orderId });
动态路由跳转:
_vid.vue
: 使用vid
接收
vid: params.vid
Nginx.bat脚本
window下Nginx启动bat脚本_一生所爱的专栏-CSDN博客_bat启动nginx
双击即可
首页课程和名师 显示
分页有bug,第一那个数字会显示所有的页数,如果有100页就显示1-100,第二道最后一页还能点>,可以加个if hasNext
完美解决 ,直接加这个条件即可 if (page<=this.data.pages&&page>0&&page!==this.data.current)
- service层有根据
QueryWrapper
的list方法
List<EduCourse> list = courseService.list(wrapper);
查询单个
EduCourse course = courseService.getOne(wrapper);
课程:
分页条件(类别、销量、时间、价格)查询
course.getConditionPage(1,8,{}).then((resp)=>{this.subSubjectList=[]this.data=resp.data.data;});
2022年2月9日22:16现在做到SQL
查询课程的ALL信息:
测试
SELECT
c.id,
c.title,
CONVERT(c.price, DECIMAL(8,2)) AS price,
c.buy_count AS buyCount,
c.lesson_num AS lessonNum,
c.view_count AS viewCount,
d.description,
sone.id AS subjectLevelOneId,,
sone.title AS subjectLevelOne,
stwo.id AS subjectLevelTwoId,
stwo.title AS subjectLevelTwo ,
t.id AS teacherId,
t.`name`AS teacherName,
t.intro,
t.avatar,
c.cover
FROM
edu_course c
LEFT JOIN edu_teacher t ON c.teacher_id = t.id
LEFT JOIN edu_course_description d ON d.id = c.id
LEFT JOIN edu_subject sone ON c.subject_parent_id = sone.id
LEFT JOIN edu_subject stwo ON c.subject_id = stwo.id
WHERE
c.id = 1192252213659774977
<select id="selectInfoWebById" resultType="com.guli.edu.vo.CourseWebVo">
SELECT
c.id,
c.title,
c.cover,
CONVERT(c.price, DECIMAL(8,2)) AS price,
c.lesson_num AS lessonNum,
c.cover,
c.buy_count AS buyCount,
c.view_count AS viewCount,
cd.description,
t.id AS teacherId,
t.name AS teacherName,
t.intro,
t.avatar,
s1.id AS subjectLevelOneId,
s1.title AS subjectLevelOne,
s2.id AS subjectLevelTwoId,
s2.title AS subjectLevelTwo
FROM
edu_course c
LEFT JOIN edu_course_description cd ON c.id = cd.id
LEFT JOIN edu_teacher t ON c.teacher_id = t.id
LEFT JOIN edu_subject s1 ON c.subject_parent_id = s1.id
LEFT JOIN edu_subject s2 ON c.subject_id = s2.id
WHERE
c.id = #{id}
</select>
-
CONVERT(c.price, DECIMAL(8,2))
-
decimal(8,2)
小数部分2位,整数部分 6位(8-2),如果超长数据库会舍弃,短了小数点用0补齐
-
CONVERT:将价格保留两位小数,整数为6位、
-
左外连接:
- 左(外)连接,左表(a_table)的记录将会全部表示出来,而右表(b_table)只会显示符合搜索条件的记录。右表记录不足的地方均为NULL。
图解MySQL 内连接、外连接、左连接、右连接、全连接……太多了_plg17的专栏-CSDN博客_左连接右连接
阿里视频播放器
Aliplayer没定义成功的,把阿里云视频播放器脚本移出标签对就可以了
阿里云播放器报错4003,地址为空,情况是获取数据的异步方法完成前mounted方法就开始执行去创建播放器
解决方案是使用promise对象,新写一个A方法来创建播放器,并规定好在后台数据拿到之后再执行A方法
课程评论
res.data.data.map
添加 课程id,课程详情页有
teacherId,课程详情页有
用户id、昵称、头像,根据token cookie获取(UCenter模块)
评论: 前端传来
@Component
@FeignClient(name = "service-ucenter",fallback = UcenterMemberFeignClientImpl.class)
public interface UcenterMemberClient {
@GetMapping("/UCenterService/ucenter/getUserInfoById/{uid}")
public UcenterMember getUserInfoById(@PathVariable String uid);
}
UcenterMemberController
:
@GetMapping("getUserInfoById/{uid}")
public UcenterMember getUserInfoById(@PathVariable String uid){
if (ObjectUtils.isEmpty(uid)){
throw new MyException(20001,"uid为空");
}
System.out.println(uid);
UcenterMember member = ucenterMemberService.getById(uid);
return member;
}
EduCommentController
@PostMapping("saveComment")
public R saveComment(@RequestBody EduComment eduComment, HttpServletRequest request){
if (ObjectUtils.isEmpty(request)){
throw new MyException(20001,"saveComment();request为空");
}
String id = JwtUtils.getMemberIdByJwtToken(request);
if (ObjectUtils.isEmpty(id)){
return R.error().message("用户未登录,请登录");
}
eduComment.setMemberId(id);
UcenterMember member = ucenterMemberClient.getUserInfoById(id);
System.out.println(member);
eduComment.setNickname(member.getNickname());
eduComment.setAvatar(member.getAvatar());
boolean save = eduCommentService.save(eduComment);
return save?R.ok().data("eduComment",eduComment):R.error();
}
课程支付:
课程点击购买,在Order表 生成订单
生成Orderid可以使用 IdWorker.getIdStr()
不能用UUID,后面数据库订单号长度会超出限制,后台的要求也不能超过32位
报错:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'eduPayLogServiceImpl': Unsatisfied dependency expressed through field 'baseMapper'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.liu.OrderService.mapper.EduPayLogMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
No qualifying bean of type ‘mapper.EduPayLogMapper
’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
是说 写了
@MapperScan("com.liu.UCenterService.mapper")
但没扫描到Mapper文件
- 可能是MapperScan的路径写错
- 没加
@Mapper
,(mybatis-plus之后理应不加的,应该是路径错了)
服务之间调用步骤:
(service-order
调用 service-edu
)
- 服务注册 都要写
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
-
service-order
的启动类:@EnableDiscoveryClient
: 表明该服务可被发现,即注册至服务中心@EnableFeignClients
: 启动 feign客户端,做到远程调用service-edu
(被调用者)的controller方法
-
在
service-order
创建 FeignClient(Feign客户端)
@FeignClient
:声明要调用服务的名:
spring.application.name=service-order
@Component
@FeignClient(name = "service-edu",fallback = EduCourseClientImpl.class)
public interface EduCourseClient {
// feign跨服务访问的@XXXmapping的路径要写全名。。
@GetMapping("/eduService/course/getCourseAllInfo/{courseId}")
public CourseAllInfoVo getCourseAllInfo( @PathVariable String courseId);
}
及其接口实现类: 用于报错回滚:
@Component
public class EduCourseClientImpl implements EduCourseClient{
@Override
public CourseAllInfoVo getCourseAllInfo(String courseId) {
System.out.println("CourseAllInfoVo对象为空 null!!");
return null;
}
}
价格的单位一般都是分,浮点数会有精度损失
乘以100
order.getTotalFee().multiply(new BigDecimal("100")).longValue()+"");
异步这个需要两个return。。
在组件(限于页面组件)每次加载之前被调用
然后再 调用方法orderLog.CreatePayQRCode
此时方法没有立即被调用
// 根据订单id生成微信支付二维码
asyncData({ params, error }) {
return orderLog.CreatePayQRCode(params.pid).then(response => {
return {
payObj: response.data.data.map
}
})
},
定时器:定时执行某方法:queryPayStatus()
// 在页面渲染之后执行
// 每隔三秒,去查询一次支付状态
this.timer1 = setInterval(() => {
this.queryPayStatus(this.payObj.out_trade_no)
}, 3000)
清除定时器:
clearInterval(this.timer1)
使用微信二维码支付后,
有个问题:
URL直接输入
http://localhost:3000/pay/20220212201711741 未登录即可访问,权限问题?
购买课程之后
查询用户是否已购买该课程:
(用户对同一课程不能重复购买)
进入课程详情页就判断用户是否已经购买过该课程来显示立即购买还是立即观看:
用户购买课程生成的订单可能有多条,有些是生成了订单没支付的, 故需根据用户id、课程id和status=1查询满足条件的个数
显示立即观看还是立即购买是在查询课程处,故需要Feign 服务调用
因为这个id是直接路由传递过来的,直接有值。方法的话要等后端传递数据可能浏览器已经有页面了但是后端还没有传递数据过来
统计分析
统计注册人数
like左通配不走索引所以不建议用like
用了函数就不走索引了,和用like gmt_create='2020-03-09%'有啥区别
MySQL中聚合函数那些事(超级详细的总结)_haust_允谦的博客-CSDN博客
可以这样去理解group by和聚合函数_shaofei63的专栏-CSDN博客_group函数
SQL HAVING 子句 | 菜鸟教程 (runoob.com)
where条件里为什么不能有聚合函数_学无止境-CSDN博客_where为什么不能用聚合函数
计划::
mapper中写的是 @Param
@Repository
public interface UcenterMemberMapper extends BaseMapper<UcenterMember> {
int selectRegisterCount(@Param("day") String day);
}
@Value
在配置类中写:
@Value("${aliyun.oss.file.endpoint}")
private String endpoint;
分库分表时需要: 服务间进行调用
required a single bean, but 2 were found:
统计注册人数分析:
统计好用户的注册人数之后,将数据 由日期 添加到 表中,但若添加之后又有新的用户注册,之后再添加
就会出现多个 同一日期的数据且 仅有 一条可用(其余数据都过时)
解决方法: 每次添加时,都先删除之前同一日期的数据。同一日期仅保留一条,保证当前数据都是最新的
cron表达式
cron表达式详解 - 沧海一粟hr - 博客园 (cnblogs.com)
Cron表达式的详细用法 - 简书 (jianshu.com)
例:
“0 15 10 ? * 6L” 每月的最后一个星期五上午10:15触发
0 15 10 ? * 5L 每个月最后一个星期四的10点15分0秒触发任务
L:表示每周最后一天(星期六)或每月最后一天
6L: 每月最后一个星期五: 星期日是第一天,开始数六下就是星期五
5L: 每个月最后一个星期四 星期日是第一天,开始数五下就是星期四
七个字符,在Springboot中不写年(只写六个)
Canal介绍
Canal就是一个很好的数据库同步工具。canal是阿里巴巴旗下的一款开源项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL。
canal是通过模拟成为mysql 的slave的方式,监听mysql 的binlog日志来获取数据,binlog设置为row模式以后,不仅能获取到执行的每一个增删改的脚本,同时还能获取到修改前和修改后的数据,基于这个特性,canal就能高性能的获取到mysql数据数据的变更。
同步数据库表:
将远程的数据库的内容 同步到本地数据库
- 当前我的linux数据库没有密码:直接敲回车就进来了
- 当前我的linux 数据库是
MariaDB
Linux:MariaDB开启binlog功能
修改 my.cnf
文件,一般来说都位于 /etc/my.cnf
这边,部分像 MariaDB 可能是修改/etc/my.cnf.d/server.cnf
文件。
在 [mysqld]
下写入内容:
远程连接linux的MariaDB
:
-
开放3306 端口
firewall-cmd --zone=public --add-port=3306/tcp --permanent #命令含义 --zone #作用域 --add-port=3306/tcp #添加端口,格式为:端口/通讯协议 --permanent #永久生效
-
重启防火墙
systemctl restart firewalld.service
- 开始连接:
Linux:MariaDB
CREATE USER 'canal'@'%' IDENTIFIED BY 'canal';
GRANT SHOW VIEW, SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;
注意:
-
Linux 的数据库、表 要与 mysql的数据库、表一致
网关:
#使用服务发现路由
spring.cloud.gateway.discovery.locator.enabled=true
- 代理服务:
#设置路由id
spring.cloud.gateway.routes[0].id=service-acl
#设置路由的uri
spring.cloud.gateway.routes[0].uri=lb://service-acl
#设置路由断言,代理servicerId为auth-service的/auth/路径
spring.cloud.gateway.routes[0].predicates= Path=/*/acl/**
#配置service-edu服务
spring.cloud.gateway.routes[1].id=service-edu
spring.cloud.gateway.routes[1].uri=lb://service-edu
spring.cloud.gateway.routes[1].predicates= Path=/eduService/**
#配置service-ucenter服务
spring.cloud.gateway.routes[2].id=service-ucenter
spring.cloud.gateway.routes[2].uri=lb://service-ucenter
spring.cloud.gateway.routes[2].predicates= Path=/UCenterService/**
spring.cloud.gateway.routes[3].id=service-cms
spring.cloud.gateway.routes[3].uri=lb://service-cms
spring.cloud.gateway.routes[3].predicates= Path=/cmsService/**
spring.cloud.gateway.routes[4].id=service-msm
spring.cloud.gateway.routes[4].uri=lb://service-msm
spring.cloud.gateway.routes[4].predicates= Path=/msmService/**
spring.cloud.gateway.routes[5].id=service-order
spring.cloud.gateway.routes[5].uri=lb://service-order
spring.cloud.gateway.routes[5].predicates= Path=/OrderService/**
spring.cloud.gateway.routes[6].id=service-oss
spring.cloud.gateway.routes[6].uri=lb://service-oss
spring.cloud.gateway.routes[6].predicates= Path=/Oss/**
spring.cloud.gateway.routes[7].id=service-statistics
spring.cloud.gateway.routes[7].uri=lb://service-statistics
spring.cloud.gateway.routes[7].predicates= Path=/StatisticService/**
spring.cloud.gateway.routes[8].id=service-vod
spring.cloud.gateway.routes[8].uri=lb://service-vod
spring.cloud.gateway.routes[8].predicates= Path=/Video/**
##配置service-ucenter服务
spring.cloud.gateway.routes[9].id=service-ucenter
spring.cloud.gateway.routes[9].uri=lb://service-ucenter
spring.cloud.gateway.routes[9].predicates= Path=/api/**
.id=service-order
.uri=lb://service-order
.predicates= Path=/OrderService/**
为什么要使用网关_不知名帅哥的博客-CSDN博客_为什么需要网关
这样可以统一使用 8010端口号来访问所有的服务:
localhost:8001/eduService/course/getCourseAllInfo/18
localhost:8010/eduService/course/getCourseAllInfo/18
- 网关的负载均衡:
- 解决跨域问题:
配置类:
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
这样就不用在服务的Controller
写 注解:@CrossOrign
;加上会报错
The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
原因:设置了两次跨域,只需要设置一次就可以。
要么 用配置类;
要么用注解@CrossOrign
SpringSecurity
本质上是 filter:过滤器
多模块情况下:用户登录采用token的方式
- 用户 使用用户名+密码 登录成功
- 获取到用户的权限值,以 (key,value)存储(用户名,权限) 存到Redis中;
- 以 用户名生成token;返回 存入前端 cookie 、header
- 用户登录后 SpringSecurity 解析header的token;
- 获取到用户名 查出Redis 获取权限列表
- 对用户进行授权
只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。
不需要序列化的属性前添加关键字transient