学习谷粒学苑实录

学习谷粒学苑实录

记:
这是我看谷粒学苑时遇到的问题以及学习的笔记。
前言:

  • 记得我看谷粒学苑的时候,每当我遇到问题(bug),在网上搜了半天,大多遇到的问题所处的环境都大不相同,照着文章的方法不一定100%解决,最坏的情况是:bug越改越大。
    • 所以 我总会想什么时候会有一篇文章 就像一学长一样,能快速指出是哪块出的问题。这就是我写这篇文章的初衷,也许它很烂,也许也并没有解决你的问题,但总的来说感谢你的观看!!

文章目录

  • 学习谷粒学苑实录
  • vue学习
  • 一、基本语法
    • 1、let声明变量
    • 2、const声明常量(只读变量)
    • 单向绑定:
    • 双向绑定
    • 组件:
      • 全局组件:
      • 局部组件:
    • axios
    • node.js
    • NPM
    • babel
    • 前端模块化
    • Webpack
    • vue模板:
      • 1、框架的结构:
      • 2、 跨域问题:
      • 3、请求机制
      • 4、开发过程:
          • 表单指明为`postMapping`,为什么却进入了get方法
    • 文件上传OOS
      • Nginx
      • 思考
    • 课程分类
      • 采用文件Excel 上传
      • vue 的api 什么时候写 data:
    • 课程
      • 1、细节问题:
      • 2、查询课程里面的章节和小节
      • 3、章节的管理
      • 4、课程的最终发布
      • 5、课程删除
      • 6、阿里云:视频点播
        • 6.1 删除视频
      • 7、微服务
        • Spring Cloud相关基础服务组件
        • 7.1 注册中心
          • Nacos
            • 三步走:
          • Feign
            • `@RequestBody`与`@RequestParam`
          • Hystrix
  • 前台(用户界面)
      • seo不使用Ajax(异步)的原因:
    • nuxt路由
  • Springboot整合Redis
    • **Redis**:
      • redis报错:
    • 单点登录
      • JWT
        • 版本冲突
        • linux系统重启后,Redis的数据消失:
        • Springboot启动scan问题
        • @MapperScan和@ComponentScan的区别
      • 用户的注册
        • copyProperties
        • Springboot测试类需要写:
      • OAuth2.0
        • 扫码
        • %s : 占位符:
        • 微信扫码流程
        • Vue获取URL参数方式
          • 路由跳转:
          • 动态路由跳转:
        • Nginx.bat脚本
    • 首页课程和名师 显示
        • 查询课程的ALL信息:
        • 阿里视频播放器
    • 课程评论
    • 课程支付:
      • 服务之间调用步骤:
      • 使用微信二维码支付后,
      • 购买课程之后
        • 查询用户是否已购买该课程:
    • 统计分析
      • 统计注册人数
        • 统计注册人数分析:
      • cron表达式
      • Canal介绍
        • **Linux:MariaDB开启binlog功能**
        • 远程连接linux的`MariaDB`:
    • 网关:
      • SpringSecurity
    • 用户认证(登录)

2021年12月15日10:03:49

忘了:

  • @MapperScan("com.liu.eduService.mapper")

    ​ // 扫描 Mapper 文件

  • @EnableTransactionManagement

    // 开启事务注解,等同于配置文件

2021年12月15日10:50:31

@DeleteMapping

记录@DeleteMapping注解的使用 - 大日很忧伤 - 博客园 (cnblogs.com)

Spring Boot @DeleteMapping - 边见众生,边见自己 - 博客园 (cnblogs.com)

在公共模块配置swagger: 为了让所有模块都能使用

又报错:

学习谷粒学苑实录_第1张图片

焯 !!!

刷一下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

  1. java将构造方法私有化:
    1. 可以使得该类不被实例化,和不能被继承。要创建这个类的实例,唯一的办法是提供一个公共静态方法。这种方式被称为单例模式。
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 ;       }

}
  1. @Accessors(chain = true) 链式编程

  2. @RequestBody 表示当前接收来自前端的JSON数据

    此时须为 @PostMapping

    因为前端不能使用GET方式提交数据,而是用POST方式进行提交

    而 POST请求中content的值必须为json格式

    @RequestBody和@RequestParam的请求方式get和post关系_qfikh的博客-CSDN博客_@requestparam post请求

学习谷粒学苑实录_第2张图片
这个gmt_modified 还真就没人注意…

三目运算符

return save?R.ok():R.error();

return teacherService.save(eduTeacher)?R.ok():R.error();

  1. 循环依赖:

面试必杀技,讲一讲Spring中的循环依赖 - 程序员DMZ - 博客园 (cnblogs.com)

2021年12月21日17:06:05

  1. // 表明 :该类是注解 式的 异常处理类
    @ControllerAdvice
    
  2. 日志的等级:

日志记录器(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)

学习谷粒学苑实录_第3张图片

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-bindtitle(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:"
  • 首页
  • " + "
  • 学员管理
  • "
    + "
  • 讲师管理
  • "
    + "
"
})

使用:

<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:"
  • abc
  • "
    } } })
    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

    1. 定义: node.js 是JavaScript的运行环境;
      • 即:不需要浏览器、服务器也可运行 JS代码

    NPM

    1. node package Manager : Node.js 包的管理工具;
      • 相当于前端的Maven,管理前端JS依赖

    【已解决】出错的兄弟可以先删除这个文件C:\Users{当前登录的用户名}.npmrc

    babel

    将es6 代码转为 ES5

    (低版本浏览器不支持ES6 [JS标准])

    学习谷粒学苑实录_第4张图片

    前端模块化

    类似于 类与类之间的调用

    JS与JS之间的调用

    模块化规范:

    • CommonJS模块化规范
    • ES6模块化规范

    Webpack

    一个前端资源打包工具

    ​ Webpack 可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求。

    vue模板:

    下载前端依赖: npm install

    遇到的错误:

    Error: Can’t find Python executable “python”, you can set the PYTHON env variable.解决办法_菜鸟karroy-CSDN博客

    启动vue项目失败,报错Failed at the [email protected] postinstall script. - xiaodangshan - 博客园 (cnblogs.com)

    再次使用命令

    1、框架的结构:

    1. 前端入口:

      index.html

      src 下面:main.js

    2. build目录:

      脚本文件

    3. config目录:

      配置: 项目端口等

      dev env.js : 修改访问后端接口地址

    学习谷粒学苑实录_第5张图片

    1. src:

    学习谷粒学苑实录_第6张图片

    学习谷粒学苑实录_第7张图片

    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

    解决方式:

    1. 注解:@CrossOrigin
    2. 网关

    在这里插入图片描述

    3、请求机制

    第一次:

    ​ 请求方式:options:预请求(判断请求是否访问成功),不会返回数据

    第二次:发起请求(带数据)

    4、开发过程:

    1. 写路由 router
    2. 写api:接收后端的controller方法
    3. view写页面

    接下来的这一段视频,我说一下我遇到的问题

    1、加了注解还是遇到了跨域问题,原因时url里面纯在空格导致url错误

    2、出现运行时异常,可能是data写错了,或者是没有用老师说的第二种方式

    3、能够访问数据,但是控制台报错,原因时config两个地址只改了一个,另一个没改

    表单指明为postMapping,为什么却进入了get方法

    Spring MVC 表单提交方式明明已经指明为post,为什么后台却进入了get方法里?-CSDN社区

    报错:

    ​ 使用的是post方法啊,接口里 使用@PostMapping注解,报错:Request method 'GET' not supported

    原因: 跨域问题: 加上 @CrossOrign

    学习谷粒学苑实录_第8张图片

    ​ 提前加上了登录验证,登录却是去生产服务器的单点验证的,然后再跳转回来,这样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服务器,但是由于访问限制,我们需要找一个代理去访问。换句话说,客户端明确知道要访问的服务器的地址,客户端把请求发送给代理,代理转发给服务器,服务器把响应传给代理,最后代理把响应传给客户端。我们可以看到客户端知道服务器是谁,但是服务器并不知道客户端是谁,这就是正向代理,隐藏了客户端的真实信息。
    • 反向代理:

      • 类似我们访问淘宝,由于访问量巨大,淘宝会使用许多台服务器(就是分布式服务器)来支持,但是每个客户端的请求到底由哪一台服务器来响应,我们需要一个代理来决定。换句话说,客户端并不知道要把请求发送给哪一台服务器,但是知道发送给哪一个代理,然后代理依据规则(响应时间,负载均衡等)决定把请求转发给哪一台服务器。可以看到,客户端并不知道他访问的服务器是谁,这就是反向代理,隐藏了服务器的真实信息。
    1. 反向代理服务器

    2. 请求转发、负载均衡、动静分离

    • 请求转发:

      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再也关过

    课程分类

    学习谷粒学苑实录_第9张图片

    子查询、 连接查询都比单张表查询费时间的多,很多时候都是为了性能牺牲空间和范式

    采用文件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、细节问题:

    学习谷粒学苑实录_第10张图片

    1. 创建vo实体类以保存表单的数据
    2. 将分步表单 提交的数据添加到数据库
      • 两张表: 课程表和课程description表
    3. 将讲师与 课程分类使用下拉列表显示
    • 课程分类做成二级联动

    bigdecimal是比float、double更高的精度

    跳转不了的是因为源码方法名不是next自己改一下

    检查没拼写错误的老哥注意methods里的方法next()写在previous()方法的上边

    在写入课程基本信息后再 跳转 添加章节 在跳转添加课程的图片、

    学习谷粒学苑实录_第11张图片

    • 执行全局异常可以把数据库中表description的字段的数据类型从text改为mediumtext或者longtext

    • 字段: subject_parent_id 数据库为空

      ·vo类没有这个字段,加上就行了

    2、查询课程里面的章节和小节

    • 在Info页面回显数据:

    从chapter页面点击上一步:

    返回时带有课程id,

    从路由获取courseID,之后查询数据

    学习谷粒学苑实录_第12张图片

    bug1:

    学习谷粒学苑实录_第13张图片

    二级分类无法显示,

    原因:在数据回显时,二级分类的列表: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、章节的管理

    学习谷粒学苑实录_第14张图片

    报错:Invalid bound statement (not found): com..service.mapper...

    mybatis-plus 报错:Invalid bound statement (not found): com..service.mapper...***_激情小马哥的博客-CSDN博客

    学习谷粒学苑实录_第15张图片

    4、课程的最终发布

    修改课程的状态status为“Normal

    学习谷粒学苑实录_第16张图片

    学习谷粒学苑实录_第17张图片

    5、课程删除

    学习谷粒学苑实录_第18张图片

    先删除视频、小节、再删除章节、再删除课程描述、课程本身

    bug1:
    在这里插入图片描述

    Missing URI template variable 'courseId' for method parameter of type String

    @DeleteMapping@PathVariable 的内容要一致!

    学习谷粒学苑实录_第19张图片

    学习谷粒学苑实录_第20张图片

    6、阿里云:视频点播

    域名: 现在2022年1月13日 播放、上传不加密视频 无需域名

    api:通过传参数即可调用

    学习谷粒学苑实录_第21张图片

    API:数据服务功能接口

    SDk:对API进行封装、更便于使用

    httpclient: 可替代浏览器对请求进行响应、发布

    • 为什么数据库存视频的是id而不是视频地址URL:
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BqohXT0j-1650549236110)(D:\Typora\yuancpan\Typora\typora-user-images\image-20220113131411498.png)]

      • 原因: 上传视频可以进行加密,而加密之后仅通过URL不可播放视频;
        • 根据 视频的id可以 获取 凭证+URL 进而播放视频
    1. 上传视频
    2. 播放视频
    3. 删除视频
    • 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 的类,在方法之外传值为空

    学习谷粒学苑实录_第22张图片

    • 视频上传跨域问题

      • 没有加注解,
      • nginx没有配置8003,
      • nginx需要配置最大文件大小,网上有具体操作
      client_max_body_size 1024m;
      

    学习谷粒学苑实录_第23张图片

    6.1 删除视频

    • 删除完课程,如果不保存直接退出,会有bug(阿里云的删除了,但数据库的仍在)

    • 删除视频后,需要将视频的id和name清空

    • 想要做回显视频的,在编辑小节的方法里获取到小节的集合之后,再加上一句

    this.fileList = [{'name': this.video.videoOriginalName}]
    

    7、微服务

    微服务就是一个项目可拆分成多个独立的,占用独立进程的服务

    学习谷粒学苑实录_第24张图片

    Spring Cloud相关基础服务组件

    服务发现——Netflix Eureka (Nacos)

    服务调用——Netflix Feign

    熔断器——Netflix Hystrix

    服务网关——Spring Cloud GateWay

    分布式配置——Spring Cloud Config (Nacos)

    消息总线 —— Spring Cloud Bus (Nacos)

    学习谷粒学苑实录_第25张图片

    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’ - 临飞 - 博客园 (cnblogs.com)

    ​ 调试代码时出现异常: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参数 的名字一致,也可以指定参数名:
        学习谷粒学苑实录_第26张图片

      • 可以设置是否必须传该参数

        • 不传递报错400的那种

    String.join(“,”,videoIdList); 直接就转了

    学习谷粒学苑实录_第27张图片

    兄弟,你应该加个过滤 .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 [email protected] --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" 是固定的
    
      课程
    
    
    • 动态路由:

      • 每次生成路由地址不一样: 课程详情页传递的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-cmsservice-edu没有导入 Redis依赖

    都导入:即可

    
    <dependency>
        <groupId>com.TestGuLigroupId>
        <artifactId>commonartifactId>
        <version>0.0.1-SNAPSHOTversion>
    dependency>
    

    单点登录

    Single Sign On,简称SSO

    单点登录(SSO)看这一篇还不够~ 这次不慌了_m0_64420350的博客-CSDN博客_sso单点登录

    • 定义:在多系统应用群中,在任意系统登录,只需要登录一次,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分

    单点登录:

    三种实现方式:

    第一种: session广播机制:

    • session复制:
      • 将一个系统的session复制到多个系统内

    学习谷粒学苑实录_第28张图片

    第二种: 使用cookie + redis 实现

    学习谷粒学苑实录_第29张图片

    第三种: Token(令牌

    token:服务端按一定规则生成的一串字符串;(可携带用户信息)

    • 作用:
      • 以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

    学习谷粒学苑实录_第30张图片

    JWT

    一种用于认证用户信息

    • 构成:
      • 头部(header):声明 类型jwt和 加密算法(通常直接使用 HMAC SHA256)
      • 载荷(playload)(中部)包含需要传递的数据
      • 签证 (signature)
        • header (base64后的)
        • payload (base64后的)
        • secret
          • 需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密

    secret是保存在服务器端的,jwt的签发生成也是在服务器端的

    所以 secret是私钥

    学习谷粒学苑实录_第31张图片

    亲测. 首页->云市场->心选市场->搜索短信服务->随便找个买家购买. 卖家商品页面有示列代码,完全可以用。我已经成功了。

    申请不通过完全可以跳过发送短信步骤,再后台打印验证,手动再输入就行了

    调用jar包:

    版本冲突

    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>fastjsonartifactId>
        <version>1.2.15version>
    dependency>
    <dependency>
        
        <groupId>org.apache.httpcomponentsgroupId>
        <artifactId>httpclientartifactId>
        <version>4.5.1version>
    dependency>
    
    <dependency>
     
        <groupId>org.apache.httpcomponentsgroupId>
        <artifactId>httpcoreartifactId>
        <version>4.3.3version>
    dependency>
     
    <dependency>
        <groupId>commons-langgroupId>
        <artifactId>commons-langartifactId>
        <version>2.6version>
    dependency>
    
    <dependency>
        <groupId>org.eclipse.jettygroupId>
        <artifactId>jetty-utilartifactId>
        <version>9.3.7.v20160115version>
    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

      取出某 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文件 引入进来 不然就报错!!!

    学习谷粒学苑实录_第32张图片

    :rules="[
    { 	required: true,  // 必须输入值
    	message: '请输入手机号码', // 提示信息
    	trigger: 'blur' 
    },	// 在 失去焦点时触发
    {	validator:  
    	checkPhone,  // 手机格式校验的方法
    	trigger: 'blur'
        }]"
    

    this.codeTest = 60;放在启动定时器前,解决延迟

    用户账号登录:

    学习谷粒学苑实录_第33张图片

    default.vue是用写 公共的头部和底部

    中间的在page文件里

    框架封装的太好,搞得大家都不明白为什么之前用的好好都不需要转,现在却需要转是为什么,大家可以想想jquery是如何处理返回的数据的,jquery需要指定返回的数据类型是json才会自动帮你转化

    OAuth2.0

    学习谷粒学苑实录_第34张图片

    简单来说,jwt的Token 是OAuth 的一种实现方式

    try catch快捷键 Ctrl + Alt + T

    扫码

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7kkiWKlF-1650549236115)(D:\Typora\yuancpan\Typora\typora-user-images\image-20220207175933434.png)]

    学习谷粒学苑实录_第35张图片

    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

    微信扫码流程

    准备工作 | 微信开放文档 (qq.com)

    扫描二维码之后,自动跳转至:

    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

    将已扫描的用户信息也传至首页

    学习谷粒学苑实录_第36张图片

    • 先由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");
    
    1. 前端提交登录信息-》后端将id和昵称封装成token返回给前端-》前端收到token并存入cookie,然后跳转到首页

    2. 首页判断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
    

    学习谷粒学苑实录_第37张图片

    路由跳转:
    this.$router.push({ path: '/order/'+  response.data.data.orderId });
    
    动态路由跳转:

    _vid.vue: 使用vid 接收

    vid:  params.vid
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DX0Ka8uk-1650549236117)(D:\Typora\yuancpan\Typora\typora-user-images\image-20220212194111073.png)]

    在这里插入图片描述

    Nginx.bat脚本

    window下Nginx启动bat脚本_一生所爱的专栏-CSDN博客_bat启动nginx

    学习谷粒学苑实录_第38张图片

    双击即可

    首页课程和名师 显示

    分页有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;});

    学习谷粒学苑实录_第39张图片

    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方法

    课程评论

    学习谷粒学苑实录_第40张图片

    学习谷粒学苑实录_第41张图片

    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表 生成订单

    学习谷粒学苑实录_第42张图片

    生成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

    1. 服务注册 都要写
    # nacos服务地址
    spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
    
    1. service-order的启动类:

      • @EnableDiscoveryClient: 表明该服务可被发现,即注册至服务中心
      • @EnableFeignClients : 启动 feign客户端,做到远程调用 service-edu(被调用者)的controller方法
    2. 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)
    

    学习谷粒学苑实录_第43张图片

    使用微信二维码支付后,

    学习谷粒学苑实录_第44张图片

    有个问题:

    URL直接输入

    http://localhost:3000/pay/20220212201711741 未登录即可访问,权限问题?

    购买课程之后

    学习谷粒学苑实录_第45张图片

    查询用户是否已购买该课程:

    (用户对同一课程不能重复购买)

    进入课程详情页就判断用户是否已经购买过该课程来显示立即购买还是立即观看:

    ​ 用户购买课程生成的订单可能有多条,有些是生成了订单没支付的, 故需根据用户id、课程id和status=1查询满足条件的个数

    显示立即观看还是立即购买是在查询课程处,故需要Feign 服务调用

    因为这个id是直接路由传递过来的,直接有值。方法的话要等后端传递数据可能浏览器已经有页面了但是后端还没有传递数据过来

    统计分析

    统计注册人数

    like左通配不走索引所以不建议用like

    用了函数就不走索引了,和用like gmt_create='2020-03-09%'有啥区别

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9HbFe0pU-1650549236121)(D:\Typora\yuancpan\Typora\typora-user-images\image-20220213221255209.png)]

    MySQL中聚合函数那些事(超级详细的总结)_haust_允谦的博客-CSDN博客

    可以这样去理解group by和聚合函数_shaofei63的专栏-CSDN博客_group函数

    SQL HAVING 子句 | 菜鸟教程 (runoob.com)

    where条件里为什么不能有聚合函数_学无止境-CSDN博客_where为什么不能用聚合函数

    计划::

    学习谷粒学苑实录_第46张图片

    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表达式生成器 (qqe2.com)

    cron表达式详解 - 沧海一粟hr - 博客园 (cnblogs.com)

    Cron表达式的详细用法 - 简书 (jianshu.com)

    例:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-coVaTfNW-1650549236122)(D:\Typora\yuancpan\Typora\typora-user-images\image-20220216200915195.png)]

    “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数据库的过程_duchenlong的博客-CSDN博客
      • Linux MariaDB管理MySQL启动、停止、查看状态_学而不厌 诲人不倦-CSDN博客_mariadb.service

    学习谷粒学苑实录_第47张图片

    Linux:MariaDB开启binlog功能

    修改 my.cnf 文件,一般来说都位于 /etc/my.cnf 这边,部分像 MariaDB 可能是修改/etc/my.cnf.d/server.cnf 文件。

    [mysqld] 下写入内容:

    学习谷粒学苑实录_第48张图片

    远程连接linux的MariaDB

    • 开放3306 端口

      firewall-cmd --zone=public --add-port=3306/tcp --permanent
      #命令含义
      --zone #作用域 
      --add-port=3306/tcp #添加端口,格式为:端口/通讯协议 
      --permanent #永久生效
      
    • 重启防火墙

    systemctl restart firewalld.service
    
    • 开始连接:

    学习谷粒学苑实录_第49张图片

    Linux:MariaDB

    学习谷粒学苑实录_第50张图片

    CREATE USER 'canal'@'%' IDENTIFIED BY 'canal';
    GRANT SHOW VIEW, SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
    FLUSH PRIVILEGES;
    

    学习谷粒学苑实录_第51张图片

    注意:

    • Linux 的数据库、表 要与 mysql的数据库、表一致
      学习谷粒学苑实录_第52张图片

      学习谷粒学苑实录_第53张图片

    网关:

    #使用服务发现路由
    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博客_为什么需要网关

    学习谷粒学苑实录_第54张图片

    这样可以统一使用 8010端口号来访问所有的服务:

    localhost:8001/eduService/course/getCourseAllInfo/18

    localhost:8010/eduService/course/getCourseAllInfo/18

    • 网关的负载均衡:

    学习谷粒学苑实录_第55张图片

    • 解决跨域问题:

    配置类:

    @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

    学习谷粒学苑实录_第56张图片

    学习谷粒学苑实录_第57张图片

    SpringSecurity

    本质上是 filter:过滤器

    • 用户认证(登录)

    • 用户授权
      • 不同的角色有不同的权限

    多模块情况下:用户登录采用token的方式

    1. 用户 使用用户名+密码 登录成功
    2. 获取到用户的权限值,以 (key,value)存储(用户名,权限) 存到Redis中;
    3. 以 用户名生成token;返回 存入前端 cookie 、header
    4. 用户登录后 SpringSecurity 解析header的token;
      • 获取到用户名 查出Redis 获取权限列表
    5. 对用户进行授权

    只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。

    不需要序列化的属性前添加关键字transient

    你可能感兴趣的:(项目记录,spring,java-ee,spring,boot)