谷粒学院

项目总结-谷粒学院(前后端分离)

1、项目描述
在线教育系统,分为前台网站系统和后台运营平台,B2C模式。
前台用户系统包括课程、讲师、问答、文章几大大部分,使用了微服务技术架构,前后端分离开发。

后端的主要技术架构是:

SpringBoot + SpringCloud + MyBatis-Plus + HttpClient + MySQL +
Maven+EasyExcel+ nginx

前端的架构是:

Node.js + Vue.js +element-ui+NUXT+ECharts

其他涉及到的中间件包括

Redis、阿里云OSS、阿里云视频点播
业务中使用了ECharts做图表展示,使用EasyExcel完成分类批量添加、注册分布式单点登录使用了JWT

项目前后端分离开发:

后端采用SpringCloud微服务架构,持久层用的是MyBatis-Plus,微服务分库设计,使用Swagger生成接口文档
接入了阿里云视频点播、阿里云OSS

系统分为前台用户系统和后台管理系统两部分:
前台用户系统包括:首页、课程、名师、问答、文章。
后台管理系统包括:讲师管理、课程分类管理、课程管理、统计分析、Banner管理、订单管理、权限管理等功能。

前端

我是用vue进行开发的

  • 使用Element-UI快速搭建模板样式

  • 使用了Nuxt轻量级框架

  • 使用node成为js的一个运行环境,将前端部署在node上

为什么使用Nuxt呢?Nuxt有什么特点会让你使用?

答:Nuxt也就是服务器渲染技术也可以充当静态站点引擎,它可以很好的解决SEO问题,SEO简称(搜索引擎优化)
比如说我前端显示一张图片,是通过ajax异步请求得到数据的,抓取工具并不会等待异步完成后才对页面内容进行抓取。
而用服务器渲染技术,我们无需等待所有的js加载完后执行,而是在服务端中通过node.js将我们查询出来的数据全部封装,
然后一次性返回到前端获得更快的内容到达时间,这就是NUXT的简单操作流程,而且都是在服务端中实现的。

NUXT的流程图

axios拦截器

axios的拦截器特性

1)request请求拦截
当我们请求的时候会检查cookie中是否有token如果有就将token设置进request的head当中,发送到服务端做单点登录的逻辑处理。

2)response响应拦截做一个全局异常处理
拦截每一个响应信息,判断状态码是否正常,如果返回报错状态码,用弹窗的形式给用户展现友好的信息,如果正常就将response返回给后面业务使用。

// 创建axios实例
const service = axios.create({
   //baseURL: 'http://qy.free.idcfengye.com/api', // api 的 base_url
  //baseURL: 'http://localhost:8210', // api 的 base_url
  baseURL: 'http://192.168.56.133:9001',
  timeout: 15000 // 请求超时时间
  
})

// http request 拦截器
service.interceptors.request.use(
  config => {
  //debugger
  if (cookie.get('guli_token')) {
    config.headers['token'] = cookie.get('guli_token');
  }
    return config
  },
  err => {
  return Promise.reject(err);
})
// http response 拦截器
service.interceptors.response.use(
  response => {

    //debugger
    if (response.data.code == 28004) {
        console.log("response.data.resultCode是28004")
        // 返回 错误代码-1 清除ticket信息并跳转到登录页面
        //debugger
        window.location.href="/login"
        return
    }else{
      if (response.data.code !== 20000) {
        //25000:订单支付中,不做任何提示
        if(response.data.code != 25000) {
          Message({
            message: response.data.message || 'error',
            type: 'error',
            duration: 5 * 1000
          })
        }
        return Promise.reject('error')//只要报错前直接抛出错误,就不会执行成功方法了
      } else {
        return response;
      }
    }
  },
  error => {
    return Promise.reject(error)
});
  • NPM
    包管理工具,类似Maven

后端

  • 使用spring boot来搭建项目的环境和部署(注解开发)。

  • 持久层用的是MyBatis-Plus用到了自动填充 逻辑删除(未实现)和乐观锁(未实现)
    自动填充创建时间和修改时间还有每当数据修改时,会将修改时间自动填充
    其他的就是调用mp的api实现增删改查嘛。

  • 使用Swagger生成接口文档

  • 后端采用SpringCloud微服务架构
    1)注册中心使用了nacos将每个微服务注册进去。
    2)使用fegin来实现每个模块之间的远程调用。
    3)使用 ribbon实现负载均衡,因为导入fegin-start的依赖会将ribbon也导入,因为fegin封装了ribbon。
    4)后台使用gateway网关,解决跨越问题,还有使用gateway的过滤器检测用户是否登录,gateway过滤器通过拦截request请求看head中是否用token,如果没有就不放通过。

  • SpringSecurity实现登录和授权

后端重点功能实现

JWT令牌:(三部分)

请求头、有效载荷、签名哈希

单点登录(SSO):(token+cookie)

用户先进行登录,如果登录成功我们将用户名用JWT随机生成字符串也就是token,再将token放到cookie当中,当发送请求时通过axios拦截器来拦截我们的request请求,如果cookie中有token,我们会将他放到request的head当中再进行发送,后端通过request的head得到token然后用JWT解析得到用户名然后到数据库查找对应的用户信息,并且将用户信息返回给前端。
在这里插入图片描述

微信扫码登录:

  • 第一步:先访问微信二维码的页面,在手机上确定登录后会调用我们的callback(回调方法) 得到一个code(相当于手机验证码)
  • 第二步:利用httpclient携带上这个code请求一个微信固定的地址
    得到access_token(访问凭证)和open_id(微信号的唯一标识)
  • 第三步:利用httpclient携带上access_token和open_id请求一个微信固定的地址,这时微信返回以json形式的用户信息再用json转换工具将其转换为用户对象,我们会先判断用户表的是否有重复的open_id如果有就说明注册了,如果没有就将当的用户信息加入到我们的用户表中,
  • 第四步:当插入成功后mp会返回用户名给我们,我们将用户名使用JWT加密生成token放到url参数的位置,重定向到首页。

在这里插入图片描述

课程详细视频在线播放功能:
上传视频:

当我们在创建小节的时候向数据库保存的不是视频的地址,而是我们请求阿里云播放器提供的第三方接口的videoId。

播放视频:

通过小节id到数据库中查找对应的videoId,然后通过videoId再访问阿里云的第三方接口得到访问凭证通过videoId和访问凭证播放视频。

为什么通过videoId和访问凭证播放视频?

答:如果说通过视频地址访问视频,只能访问未加密视频,但是不能访问加密视频, 如果用videoId和访问凭证,就能访问加密视频。

    @Autowired
    private VODProperties vodProperties;
     //通过vid拿到视频凭证
     public R getVODvid( String vid) throws Exception{
        DefaultAcsClient client = InitObject.initVodClient(vodProperties.getKeyid(),vodProperties.getKeysecret());
        //请求
        GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();
        request.setVideoId(vid);

        //响应
        GetVideoPlayAuthResponse response = null;
        response = client.getAcsResponse(request);

        //得到播放凭证
        String playAuth = response.getPlayAuth();

        //返回结果
        return R.ok().message("获取凭证成功").data("playAuth", playAuth);
    }
    /**
     * 流式上传接口
     *
     * @param accessKeyId
     * @param accessKeySecret
     * @param title
     * @param fileName
     * @param inputStream
     */
    private static String testUploadStream(String accessKeyId, String accessKeySecret, String title, String fileName, InputStream inputStream) {
        UploadStreamRequest request = new UploadStreamRequest(accessKeyId, accessKeySecret, title, fileName, inputStream);
        UploadVideoImpl uploader = new UploadVideoImpl();
        UploadStreamResponse response = uploader.uploadStream(request);
        if (response.isSuccess()) {
            return response.getVideoId();
        } else { //如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因
            return response.getVideoId();
        }
    }

微信支付功能:(建议将订单加入到redis当中缓存,设置过期时间)

  • 根据用户id到订单表查询是否有和当前课程对应的消费的记录,如果有说明已购买和立即观看
  • 如果没有,会提醒支付观看我们会转跳到二维码支付页面,然后将订单信息加入到订单表
  • 前端设置定时器每三秒访问我们的接口访问订单状态,如果支付成功将订单状态改为支付成功, 并且将支付信息保存的订单日志表中。

在这里插入图片描述

前台热点信息,使用redis缓存:
原理:

  • 设置常量key比如说如果我们要存课程的热点信息,那我们就将key设置course,每次查询前使用key查询在redis中先查询是否存在信息,如果存在就直接从redis当中获取。
  • 从redis中获取的是二进制字节,然后利用对象流反序列化返回对象。
    如果redis中不存在,那么我们就向数据库中查询数据,先利用对象流将我们的对象序列化成 二进制字节,然后存入redis当中,这样下一次我们用就会直接从redis当中查询了。
    在这里插入图片描述

先集成spring-boot-starter-data-redis然后在查询的方法上打上**@Cacheable(value = “teacher”,key = “‘teacherList’”)** value为redis的存放的key, key为存放的数据名。

@Cacheable(value = "teacher",key = "'teacherList'")
@Override
public List<Teacher> getTeacherData() {
    QueryWrapper<Teacher> teacherQueryWrapper = new QueryWrapper<>();
    teacherQueryWrapper.orderByDesc("id");
    teacherQueryWrapper.last("limit 4");
    List<Teacher> teacherList = baseMapper.selectList(teacherQueryWrapper);
    return teacherList;
}

通过gateway网关的过滤器来查看request的head当中是否有token,检查是否有登录。

使用springsecurity实现认证和授权:

1)认证(登录):

1、如果登录成功。
2、先利用用户名查出对应的用户权限列表加入redis当中,然后将用户名用JWT令牌加密为token。
3、通过登录过滤器然后将token设置到cookie当中每次请求再从cookie中拿到放到request的head当中。

2)用户授权

授权过滤器先从request的head中拿到token再用JWT解析得到用户名通过用户名做为key到redis找到用户权限列表,然后给当前用户添加权限。
在这里插入图片描述

Redis做缓存,什么样的数据适合使用Redis做缓存?

经常访问,但不经常修改的数据;如主页

项目遇到的问题

跨域问题:
在这里插入图片描述

vue不管路由跳转了几次 created()方法只会执行一次
解决方法:vue监听

      watch:{
     $route(to,from){
       init()
     }
  }

mybatisPuls的xml问题:打包时不会将xml文件也打包
解决:

<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

配置文件:mybatis-plus.mapper-locations=classpath:com/atguigu/edu/mapper/xml/*.xml   
  • 用@RequestBody接受数据必须用@PostMapping提交否则报miss

  • nginx也会限制文件上传大小 总结:服务器大多都会限制文件上传大小,上传前要设置文件上传大小

  • 在这里插入图片描述

Logo

前往低代码交流专区

更多推荐