5.1 内容管理模块 - 课程预览、提交审核

news2024/12/23 9:41:48

内容管理模块 - 课程预览、提交审核

文章目录

  • 内容管理模块 - 课程预览、提交审核
  • 一、课程预览
    • 1.1 需求分析
    • 1.2 freemarker 模板引擎
      • 1.2.1 Maven 坐标
      • 1.2.2 freemaker 相关配置信息
      • 1.2.3 添加模板
    • 1.3 测试静态页面
      • 1.3.1 部署Nginx
      • 1.3.2 解决端口问题被占用问题
      • 1.3.3 配置host文件
      • 1.3.4 文件服务器
      • 1.3.5 视频播放页面
    • 1.4 课程预览 主页
      • 1.4.1 CoursePublishController
      • 1.4.2 Nginx 配置反向代理
      • 1.4.3 CoursePublishServiceImpl
      • 1.4.4 编写模板
      • 1.4.5 测试
    • 1.5 课程预览 视频
      • 1.5.1 配置Nginx
      • 1.5.2 CourseOpenController
      • 1.5.3 MediaOpenController
      • 1.5.4 测试
  • 二、提交课程审核
    • 2.1 需求分析
    • 2.2 数据模型
      • 2.2.1 课程预发布表 course_publish_pre
    • 2.3 CoursePublishController
    • 2.4 CoursePublishServiceImpl
    • 2.5 测试

一、课程预览

课程预览就是把课程的相关信息进行整合,在课程预览界面进行展示,课程预览界面与课程发布的课程详情界面一致

image-20240114145730567

1.1 需求分析

客户可以通过课程预览页面查看信息是否存在问题

如下课程预览的数据来源

image-20240114144257854

下图显示了整个课程预览的流程图

image-20240114144459438

也就是说现在怎么给前端返回一个页面呢

最最最原始的JSP可以,一些模板引擎技术也可以

前端请求内容管理服务后,后台服务系统要返回一个页面

最终要返回的页面如下图所示(预览页面,如果客户成功确认发布后,发布后的页面和预览时的页面相同)

image-20240114144449211

说明如下

1、点击课程预览,通过Nginx、后台服务网关请求内容管理服务进行课程预览。

2、内容管理服务查询课程相关信息进行整合,并通过模板引擎技术在服务端渲染生成页面,返回给浏览器。

3、通过课程预览页面点击”马上学习“打开视频播放页面。

4、视频播放页面通过Nginx请求后台服务网关,查询课程信息展示课程计划目录,请求媒资服务查询课程计划绑定的视频文件地址,在线浏览播放视频。

在这里我们用到了两个技术,一个Nginx,一个freemarker

freemarker可以在服务端生成网页然后返回给浏览器

Nginx是一个代理服务器,所有的请求都请求到Nginx上,由Nginx进行代理,然后再由Nginx发送到后台服务上

1.2 freemarker 模板引擎

freemarker提供很多指令用于解析各种类型的数据模型

参考地址:http://freemarker.foofun.cn/ref_directives.html

什么是模板引擎

早期采用的jsp技术就是一种模板引擎技术

image-20240114145255995

1、浏览器请求web服务器

2、服务器渲染页面,渲染的过程就是向jsp页面(模板)内填充数据(模型)。

3、服务器将渲染生成的页面返回给浏览器。

所以模板引擎就是:模板+数据=输出,Jsp页面就是模板,页面中嵌入的jsp标签就是数据,两者相结合输出html网页

模板引擎:服务端生成页面的一项技术

常用的java模板引擎还有哪些

Jsp、Freemarker、Thymeleaf 、Velocity 等。

Freemarker官方地址:http://freemarker.foofun.cn/

FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。FreeMarker 是免费的, 基于Apache许可证2.0版本发布。

1.2.1 Maven 坐标

在content-api工程中添加如下所示的坐标

<!-- Spring Boot 对结果视图 Freemarker 集成 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

1.2.2 freemaker 相关配置信息

freemaker 相关配置信息,我们配置在了nacos的freemarker-config-dev.yaml配置文件中

spring:
  freemarker:
    enabled: true
    cache: false   #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0
    suffix: .ftl   #页面模板后缀名
    charset: UTF-8
    template-loader-path: classpath:/templates/   #页面模板位置(默认为 classpath:/templates/)
    resources:
      add-mappings: false   #关闭项目中的静态资源映射(static、resources文件夹下的资源)

在content-api工程的bootstrap配置文件引入freemarker-config-dev.yaml配置文件

#微服务配置
spring:
  application:
    name: content-api # 项目名
  cloud:
    nacos:
      server-addr: 192.168.101.65:8848 #Nacos地址
      discovery: #服务发现(服务注册)
        namespace: dev #命名空间
        group: xuecheng-plus-project #组别
      config: # 配置中心
        ...............中间省略了好多配置
        shared-configs: #公用配置
          - data-id: freemarker-config-dev.yaml
            group: xuecheng-plus-common
            refresh: true

1.2.3 添加模板

模板其实就是一个HTML页面+数据模型

${name} 就是数据模型,也就是后台的模型数据

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
Hello ${name}!
</body>
</html>

后台准备模型数据

// 不要使用RestController,因为返回的是JSON数据,这里我们一定不要返回JSON数据
@Controller
public class FreemarkerController {
    /**
     *ModelAndView 模型和数据
     */
    @GetMapping("/testfreemarker")
    public ModelAndView test(){
        ModelAndView modelAndView = new ModelAndView();
        //指定模型数据
        modelAndView.addObject("name","小明");
        // 指定模型视图
        // 返回的是哪个视图,且不用加后缀.ftl(文件名test.ftl)
        // 因为我们已经在配置文件中告诉框架我们文件的后缀名称是什么了spring.freemarker.suffix=.ftl
        modelAndView.setViewName("test");
        return modelAndView;
    }
}

测试效果

还是能展示出来的

image-20240114151839997

1.3 测试静态页面

准备Nginx和门户

1.3.1 部署Nginx

在本机部署Nginx

server {
        listen       80;
        server_name  www.51xuecheng.cn localhost;
        #rewrite ^(.*) https://$server_name$1 permanent;
        #charset koi8-r;
        ssi on;
        ssi_silent_errors on;
        #access_log  logs/host.access.log  main;
        location / {
            alias    D:/zhangjingqi/Note/xuecheng/Code/xc-ui-pc-static-portal/xc-ui-pc-static-portal/;
            index  index.html index.htm;
        }
        #静态资源
        location /static/img/ {  
                alias  D:/zhangjingqi/Note/xuecheng/Code/xc-ui-pc-static-portal/xc-ui-pc-static-portal/img/;
        } 
        location /static/css/ {  
                alias   D:/zhangjingqi/Note/xuecheng/Code/xc-ui-pc-static-portal/xc-ui-pc-static-portal/css/;
        } 
        location /static/js/ {  
                alias   D:/itcast2022/xc_edu3.0/code_1/xc-ui-pc-static-portal/js/;
        } 
        location /static/plugins/ {  
                alias   D:/zhangjingqi/Note/xuecheng/Code/xc-ui-pc-static-portal/xc-ui-pc-static-portal/plugins/;
                add_header Access-Control-Allow-Origin http://ucenter.51xuecheng.cn;  
                add_header Access-Control-Allow-Credentials true;  
                add_header Access-Control-Allow-Methods GET;
        } 
        location /plugins/ {  
                alias   D:/zhangjingqi/Note/xuecheng/Code/xc-ui-pc-static-portal/xc-ui-pc-static-portal/plugins/;
        } 

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root

    }

修改过后将Nginx服务进行重启

1.3.2 解决端口问题被占用问题

刚开始的时候80端口一直被占用,从任务浏览器中根本结束不掉对应的进程

然后根据下面两篇文章解决掉了

文章1:

https://blog.csdn.net/qq_34272964/article/details/105207651

如果文章1不能解决的话看一下文章2,我是文章2解决的

https://blog.csdn.net/weixin_43833851/article/details/130188302

启动Nginx访问80端口,如果出现下图所示的界面,说明端口问题已经被解决了

image-20240114155645007

image-20240114161836889

假如说按照1.3.1设置了Nginx配置的话,打开网页会是如下页面

image-20240114164237341

主要访问一下预览界面http://www.51xuecheng.cn/course/course_template.html

image-20240114164342143

1.3.3 配置host文件

C:\Windows\System32\drivers\etc目录下有一个host文件

image-20240114161013919

将下面的内容粘贴到里面即可

127.0.0.1 www.51xuecheng.cn 51xuecheng.cn ucenter.51xuecheng.cn teacher.51xuecheng.cn file.51xuecheng.cn

Centos7操作系统的hosts文件在/etc目录下

当我们输入一个域名的时候,最红需要找到这个域名对应的服务器,也就是ip地址,首先会从host文件中中找

比如我们输入 www.51xuecheng.cn域名,首先会从host文件找,然后找到了,对应的ip是127.0.0.1

假如说在host文件中没有找到域名www.51xuecheng.cn对应的ip,那就会再从dns服务器找,请求到dns服务器进行解析,在dns服务器中就记录了ip和域名的对应关系

1.3.4 文件服务器

在进行课程预览时需要展示课程的图片,在线插放课程视频,课程图片、视频这些都在MinIO文件系统存储,下边统一由Nginx代理,通过文件服务域名统一访问。如下图

image-20240114164454502

我们使用的分布式文件系统MinIO来存储的文件,并且MinIO有许多的结点

我们打算前端的请求都请求到Nginx,然后由Nginx代理请求到MinIO中

比如获取一个图片,我们可以先请求到Nginx,然后再由Nginx请求到MinIO中获取图片

host文件配置如下所示信息

127.0.0.1 www.51xuecheng.cn file.51xuecheng.cn

nginx.conf中配置文件服务器的代理地址

相当于nginx.conf中再来一套配置

其实请求/video的时候就会代理到 server 192.168.101.65:9000服务器去请求资源

   #文件服务
  upstream fileserver{
    server 192.168.101.65:9000 weight=10;
  } 
   server {
        listen       80;
        server_name  file.51xuecheng.cn;
        #charset koi8-r;
        ssi on;
        ssi_silent_errors on;
        #access_log  logs/host.access.log  main;
        location /video {
            proxy_pass   http://fileserver;
        }

        location /mediafiles {
            proxy_pass   http://fileserver;
        }
   }

image-20240114165315288

配置完毕,重新加载nginx配置文件

通过cmd进入nginx.exe所在目录,运行如下命令

nginx.exe -s reload

访问:http://file.51xuecheng.cn/mediafiles/2022/09/13/a16da7a132559daf9e1193166b3e7f52.jpg

如下所示效果

image-20240114165854981

1.3.5 视频播放页面

首先在nginx.conf中配置视频播放页面的地址

        location /course/preview/learning.html {
                alias D:/zhangjingqi/Note/xuecheng/Code/xc-ui-pc-static-portal/xc-ui-pc-static-portal/course/learning.html;
        } 
        location /course/search.html {  
                root    D:/zhangjingqi/Note/xuecheng/Code/xc-ui-pc-static-portal/xc-ui-pc-static-portal;
        } 
        location /course/learning.html {  
                root    D:/zhangjingqi/Note/xuecheng/Code/xc-ui-pc-static-portal/xc-ui-pc-static-portal;
        }

image-20240114170136975

重新加载配置文件

之后

下边需要配置learning.html页面的视频播放路径来测试视频播放页面,找到learning.html页面中videoObject对象的定义处,配置视频的播放地址

image-20240114170429866

换成minio系统中一个可以播放的地址

image-20240114170725677

1.4 课程预览 主页

课程预览接口要将课程信息进行整合,在服务端渲染页面后返回浏览器。

下边对课程预览接口进行分析:

1、请求参数

传入课程id,表示要预览哪一门课程。

2、响应结果

输出课程详情页面到浏览器

响应页面到浏览器使用freemarker模板引擎技术实现,首先从课程资料目录下获取课程预览页面course_template.html,拷贝至内容管理的接口工程的resources/templates下,并将其在本目录复制一份命名为course_template.ftl

也就是说下图中的整个页面都是模板内容

image-20240114200939485

其实就是下图所示的四部分信息

image-20240114210819041

1.4.1 CoursePublishController

@Controller
public class CoursePublishController {

    @Autowired
    CoursePublishServiceImpl coursePublishService;

    @GetMapping("/coursepreview/{courseId}")
    public ModelAndView preview(@PathVariable("courseId") Long courseId) {

        ModelAndView modelAndView = new ModelAndView();
        // 从数据库查询模型的数据(课程营销信息表、课程师资表、课程基本信息表、课程计划)
        CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId);
        // 指定模型数据
        modelAndView.addObject("model", coursePreviewInfo);
        // 指定模板
        modelAndView.setViewName("course_template");
        return modelAndView;
    }

}

其中需要的课程预览模型类

/**
 * @description 课程预览数据模型
 */
 @Data
 @ToString
public class CoursePreviewDto {

    //课程基本信息,课程营销信息
    CourseBaseInfoDto courseBase;


    //课程计划信息
    List<TeachplanDto> teachplans;
    
    //师资信息暂时不加...


}

1.4.2 Nginx 配置反向代理

课程预览接口虽然可以正常访问,但是页面没有样式,查看浏览器请求记录,发现图片、样式无法正常访问

这些静态资源全在门户下,我们需要由Nginx反向代理访问课程预览接口,通过门户的URL去访问课程预览

Nginx中如下所示配置

  upstream gatewayserver{
    server 127.0.0.1:63010 weight=10;
  } 
  server {
        listen       80;
        server_name  www.51xuecheng.cn localhost;
        ....
        #api
        location /api/ {
                proxy_pass http://gatewayserver/;
   } 

之后重新启动Nginx

image-20240114203228126

访问地址: http://www.51xuecheng.cn/api/content/coursepreview/74

出现的场景如下图所示

image-20240114210244573

现在的请求其实是下面所示的流程:

image-20240114210441194

1.4.3 CoursePublishServiceImpl

/**
 * 课程发布相关业务
 */
@Service
public class CoursePublishServiceImpl implements CoursePublishService {

    //课程基础信息(课程营销信息表/课程基本信息表)
    @Autowired
    CourseBaseInfoService courseBaseInfoService;

    //课程计划
    @Autowired
    TeachplanService teachplanService;

    /**
     * @param courseId 课程id
     * @description 获取课程预览信息
     */
    @Override
    public CoursePreviewDto getCoursePreviewInfo(Long courseId) {
        // 从数据库查询模型的数据(课程营销信息表、课程师资表、课程基本信息表、课程计划)
        // 课程基本信息,营销信息
        CourseBaseInfoDto courseBaseInfoDto = courseBaseInfoService.getCourseBaseInfo(courseId);

        //课程计划信息
        List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId);

        // 组装返回信息
        CoursePreviewDto coursePreviewDto = new CoursePreviewDto();
        coursePreviewDto.setCourseBase(courseBaseInfoDto);
        coursePreviewDto.setTeachplans(teachplanTree);
        return coursePreviewDto;
    }
}

1.4.4 编写模板

模型数据准备好后下一步将模型数据填充到course_template.ftl上,填充时注意不要一次填充太多,一边填充一边刷新调试。

freemarker提供很多指令用于解析各种类型的数据模型

参考地址:http://freemarker.foofun.cn/ref_directives.html

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="/static/img/asset-favicon.ico">
    <title>学成在线-${model.courseBase.name}</title>

    <link rel="stylesheet" href="/static/plugins/normalize-css/normalize.css"/>
    <link rel="stylesheet" href="/static/plugins/bootstrap/dist/css/bootstrap.css"/>
    <link rel="stylesheet" href="/static/css/page-learing-article.css"/>
</head>

<body data-spy="scroll" data-target="#articleNavbar" data-offset="150">
<!-- 页面头部 -->
<!--#include virtual="/include/header.html"-->
<!--页面头部结束sss-->
<div id="learningArea">
    <div class="article-banner">
        <div class="banner-bg"></div>
        <div class="banner-info">
            <div class="banner-left">
                <p>${model.courseBase.mtName!''}<span>\ ${model.courseBase.stName!''}</span></p>
                <p class="tit">${model.courseBase.name}</p>
                <p class="pic">
                    <#if model.courseBase.charge=='201000'>
                        <span class="new-pic">免费</span>
                    <#else>
                        <span class="new-pic">特惠价格¥${model.courseBase.price!''}</span>
                        <span class="old-pic">原价¥${model.courseBase.originalPrice!''}</span>
                    </#if>
                </p>
                <p class="info">
                    <a href="#" @click.prevent="startLearning()">马上学习</a>
                    <span><em>难度等级</em>
                <#if model.courseBase.grade=='204001'>
                    初级
                <#elseif model.courseBase.grade=='204002'>
                    中级
                <#elseif model.courseBase.grade=='204003'>
                    高级
                </#if>
                </span>
                    <span><em>课程时长</em>2小时27分</span>
                    <span><em>评分</em>4.7分</span>
                    <span><em>授课模式</em>
                 <#if model.courseBase.teachmode=='200002'>
                     录播
                 <#elseif model.courseBase.teachmode=='200003'>
                     直播
                 </#if>
                </span>
                </p>
            </div>
            <div class="banner-rit">
                <p>
                    <a href="http://www.51xuecheng.cn/course/preview/learning.html?id=${model.courseBase.id}"
                       target="_blank">
                        <#if model.courseBase.pic??>
                            <img src="http://file.51xuecheng.cn${model.courseBase.pic}" alt="" width="270" height="156">
                        <#else>
                            <img src="/static/img/widget-video.png" alt="" width="270" height="156">
                        </#if>

                    </a>
                </p>
                <p class="vid-act"><span> <i class="i-heart"></i>收藏 23 </span> <span>分享 <i class="i-weixin"></i><i
                                class="i-qq"></i></span></p>
            </div>
        </div>
    </div>
    <div class="article-cont">
        <div class="tit-list">
            <a href="javascript:;" id="articleClass" class="active">课程介绍</a>
            <a href="javascript:;" id="articleItem">目录</a>
            <a href="javascript:;" id="artcleAsk">问答</a>
            <a href="javascript:;" id="artcleNot">笔记</a>
            <a href="javascript:;" id="artcleCod">评价</a>
            <!--<div class="down-fill">
                <span>资料下载</span>
                <ul>
                    <li>java视频资料</li>
                    <li>java视频资料</li>
                    <li>java视频资料</li>
                </ul>
            </div>-->
        </div>
        <div class="article-box">
            <div class="articleClass" style="display: block">
                <!--<div class="rit-title">评价</div>-->
                <div class="article-cont">
                    <div class="article-left-box">
                        <div class="content">

                            <div class="content-com suit">
                                <div class="title"><span>适用人群</span></div>
                                <div class="cont cktop">
                                    <div>
                                        <p>${model.courseBase.users!""}</p>
                                    </div>
                                    <!--<span class="on-off">更多 <i class="i-chevron-bot"></i></span>-->
                                </div>
                            </div>
                            <div class="content-com course">
                                <div class="title"><span>课程制作</span></div>
                                <div class="cont">
                                    <div class="img-box"><img src="/static/img/widget-myImg.jpg" alt=""></div>
                                    <div class="info-box">
                                        <p class="name">教学方:<em>XX老师</em></p>
                                        <!-- <p class="lab">高级前端开发工程师 10年开发经验</p>-->
                                        <p class="info">
                                            JavaEE开发与教学多年,精通JavaEE技术体系,对流行框架JQuery、DWR、Struts1/2,Hibernate,Spring,MyBatis、JBPM、Lucene等有深入研究。授课逻辑严谨,条理清晰,注重学生独立解决问题的能力。</p>
                                        <!-- <p><span>难度等级</span>中级</p>
                                         <p><span>课程时长</span>8-16小时/周,共4周</p>
                                         <p><span>如何通过</span>通过所有的作业及考核,作业共4份,考核为一次终极考核</p>
                                         <p><span>用户评分</span>平均用户评分 <em>4.9</em> <a href="#">查看全部评价</a></p>
                                         <p><span>课程价格</span>特惠价格<em>¥999</em> <i> 原价1999 </i></p>-->
                                    </div>
                                </div>

                            </div>
                            <div class="content-com about">
                                <div class="title"><span>课程介绍</span></div>
                                <div class="cont cktop">
                                    <div>
                                        <p>${model.courseBase.description!""}</p>
                                    </div>
                                    <!--<span class="on-off">更多 <i class="i-chevron-bot"></i></span>-->
                                </div>
                            </div>
                            <div class="content-com prob">
                                <div class="title"><span>常见问题</span></div>
                                <div class="cont">
                                    <ul>
                                        <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 我什么时候能够访问课程视频与作业?</span>
                                            <div class="drop-down">
                                                <p>
                                                    课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                            </div>
                                        </li>
                                        <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 如何需要额外的时间来完成课程会怎么样?</span>
                                            <div class="drop-down">
                                                <p>
                                                    课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                            </div>
                                        </li>
                                        <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 我支付次课程之后会得到什么?</span>
                                            <div class="drop-down">
                                                <p>
                                                    课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                            </div>
                                        </li>
                                        <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 退款条例是如何规定的?</span>
                                            <div class="drop-down">
                                                <p>
                                                    课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                            </div>
                                        </li>
                                        <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 有助学金?</span>
                                            <div class="drop-down">
                                                <p>
                                                    课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                            </div>
                                        </li>
                                    </ul>
                                </div>
                            </div>
                        </div>
                    </div>

                    <!--侧边栏-->
                    <!--#include virtual="/include/course_detail_side.html"-->
                    <!--侧边栏-->

                </div>
            </div>
            <div class="articleItem" style="display: none">
                <div class="article-cont-catalog">
                    <div class="article-left-box">
                        <div class="content">
                            <#list model.teachplans as firstNode>
                                <div class="item">
                                    <div class="title act"><i class="i-chevron-top"></i>${firstNode.pname}<span
                                                class="time">x小时</span></div>
                                    <div class="drop-down" style="height: 260px;">
                                        <ul class="list-box">
                                            <#list firstNode.teachPlanTreeNodes as secondNode>
                                                <li>
                                                    <a href="http://www.51xuecheng.cn/course/preview/learning.html?id=${model.courseBase.id}&chapter=${secondNode.teachplanMedia.teachplanId!''}"
                                                       target="_blank">${secondNode.pname}</a></li>
                                            </#list>
                                        </ul>
                                    </div>
                                </div>
                            </#list>
                            <#-- <div class="item">
                                 <div class="title act"><i class="i-chevron-top"></i>第一阶段 HTTP协议基础详解<span class="time">8小时</span></div>
                                 <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                                 <div class="drop-down" style="height: 260px;">
                                     <ul class="list-box">
                                         <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                         <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                         <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                         <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                         <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                         <li>阶段测试</li>
                                     </ul>
                                 </div>
                             </div>-->


                        </div>
                    </div>
                    <!--侧边栏-->
                    <!--#include virtual="/include/course_detail_side.html"-->
                    <!--侧边栏-->
                </div>
            </div>
            <#--<div class="articleItem" style="display: none">
                <div class="article-cont-catalog">
                    <div class="article-left-box">
                        <div class="content">
                            <div class="item">
                                <div class="title act"><i class="i-chevron-top"></i>第一阶段 HTTP协议基础详解<span class="time">8小时</span></div>
                                <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                                <div class="drop-down" style="height: 260px;">
                                    <ul class="list-box">
                                        <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                        <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                        <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                        <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                        <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                        <li>阶段测试</li>
                                    </ul>
                                </div>
                            </div>
                            <div class="item">
                                <div class="title"><i class="i-chevron-bot"></i>第二阶段 HTTP协议基础详解<span class="time">8小时</span></div>
                                <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                                <div class="drop-down">
                                    <ul class="list-box">
                                        <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                        <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                        <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                        <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                        <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                        <li>阶段测试</li>
                                    </ul>
                                </div>
                            </div>
                            <div class="item">
                                <div class="title"><i class="i-chevron-bot"></i>第三阶段 HTTP协议基础详解<span class="time">3小时</span></div>
                                <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                                <div class="drop-down">
                                    <ul class="list-box">
                                        <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                        <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                        <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                        <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                        <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                        <li>阶段测试</li>
                                    </ul>
                                </div>
                            </div>
                            <div class="item">
                                <div class="title"><i class="i-chevron-bot"></i>第四阶段 HTTP协议基础详解<span class="time">3小时</span></div>
                                <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                                <div class="drop-down">
                                    <ul class="list-box">
                                        <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                        <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                        <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                        <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                        <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                        <li>阶段测试</li>
                                    </ul>
                                </div>
                            </div>
                            <div class="item">
                                <div class="title"><i class="i-chevron-bot"></i>第五阶段 HTTP协议基础详解<span class="time">3小时</span></div>
                                <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                                <div class="drop-down">
                                    <ul class="list-box">
                                        <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                        <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                        <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                        <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                        <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                        <li>阶段测试</li>
                                    </ul>
                                </div>

                            </div>
                            <div class="item">
                                <a href="#" class="overwrite">毕业考核</a>
                            </div>
                        </div>
                    </div>
                    <!--侧边栏&ndash;&gt;
                    <!--#include virtual="/include/course_detail_side.html"&ndash;&gt;
                    <!--侧边栏&ndash;&gt;
                </div>
            </div>-->
            <div class="artcleAsk" style="display: none">
                <div class="article-cont-ask">
                    <div class="article-left-box">
                        <div class="content">
                            <div class="content-title">
                                <p><a class="all">全部</a><a>精选</a><a>我的</a></p>
                                <p>
                                    <a class="all">全部</a><span><a>1.1</a><a>1.2</a><a>1.3</a><a>1.4</a><a>1.5</a></span><a
                                            href="$" class="more">更多 <i class="i-chevron-bot"></i></a></p>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <p class="title">如何用微服务重构应用程序?</p>
                                    <p><span>我来回答</span></p>
                                    <p>2017-3-20 <span><i></i>回答2</span><span><i></i>浏览2</span></p>
                                </div>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <p class="title">如何用微服务重构应用程序?</p>
                                    <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i
                                                class="new">心跳347890</i> 的回答】</p>
                                    <p>2017-3-20 <span class="action-box"><span><i
                                                        class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                    </p>
                                </div>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <p class="title">如何用微服务重构应用程序?</p>
                                    <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i
                                                class="new">心跳347890</i> 的回答】</p>
                                    <p>2017-3-20 <span class="action-box"><span><i
                                                        class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                    </p>
                                </div>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <p class="title">如何用微服务重构应用程序?</p>
                                    <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i
                                                class="new">心跳347890</i> 的回答】</p>
                                    <p>2017-3-20 <span class="action-box"><span><i
                                                        class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                    </p>
                                </div>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <p class="title">如何用微服务重构应用程序?</p>
                                    <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i
                                                class="new">心跳347890</i> 的回答】</p>
                                    <p>2017-3-20 <span class="action-box"><span><i
                                                        class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                    </p>
                                </div>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <p class="title">如何用微服务重构应用程序?</p>
                                    <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i
                                                class="new">心跳347890</i> 的回答】</p>
                                    <p>2017-3-20 <span class="action-box"><span><i
                                                        class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                    </p>
                                </div>
                            </div>

                            <div class="itemlast">
                                <a href="#" class="overwrite">显示更多问题</a>
                            </div>
                        </div>
                    </div>
                    <!--侧边栏-->
                    <!--#include virtual="/include/course_detail_side.html"-->
                    <!--侧边栏-->
                </div>
            </div>
            <div class="artcleNot" style="display: none;">
                <div class="article-cont-note">
                    <div class="article-left-box">
                        <div class="content">
                            <div class="content-title">
                                <p><a class="all">全部</a><a>精选</a><a>我的</a></p>
                                <p>
                                    <a class="all">全部</a><span><a>1.1</a><a>1.2</a><a>1.3</a><a>1.4</a><a>1.5</a></span><a
                                            href="$" class="more">更多 <i class="i-chevron-bot"></i></a></p>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <span class="video-time"><i class="i-play"></i>2`10`</span>
                                    <p><img src="/static/img/widget-demo.png" width="221" alt=""></p>
                                    <p class="action-box">4小时前 <span class="active-box"><span><i
                                                        class="i-coll"></i>采集</span><span><i class="i-laud"></i></span></span>
                                    </p>
                                </div>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <p>在讨论如何将重构转化为微服务之前,退后一步,<br>仔细观察微服务的内容和时间是很重要的。<br>以下两个要点将会对任何微服务重构策略产生重大影响。 </p>
                                    <p class="action-box">4小时前 <span class="active-box"><span><i
                                                        class="i-edt"></i>编辑</span><span><i
                                                        class="i-del"></i>删除</span><span><i class="i-laud"></i></span></span>
                                    </p>
                                </div>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <p>在讨论如何将重构转化为微服务之前,退后一步,<br>仔细观察微服务的内容和时间是很重要的。<br>以下两个要点将会对任何微服务重构策略产生重大影响。 </p>
                                    <p class="action-box">4小时前 <span class="active-box"><span><i
                                                        class="i-edt"></i>编辑</span><span><i
                                                        class="i-del"></i>删除</span><span><i class="i-laud"></i></span></span>
                                    </p>
                                </div>
                            </div>
                            <div class="item">
                                <div class="item-left">
                                    <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                    <p>毛老师</p>
                                </div>
                                <div class="item-right">
                                    <p>在讨论如何将重构转化为微服务之前,退后一步,<br>仔细观察微服务的内容和时间是很重要的。<br>以下两个要点将会对任何微服务重构策略产生重大影响。 </p>
                                    <p class="action-box">4小时前 <span class="active-box"><span><i
                                                        class="i-edt"></i>编辑</span><span><i
                                                        class="i-del"></i>删除</span><span><i class="i-laud"></i></span></span>
                                    </p>
                                </div>
                            </div>
                        </div>
                    </div>
                    <!--侧边栏-->
                    <!--#include virtual="/include/course_detail_side.html"-->
                    <!--侧边栏-->
                </div>
            </div>
            <div class="artcleCod" style="display: none;">
                <div class="article-cont">
                    <div class="article-left-box">
                        <div class="comment-box">
                            <div class="evaluate">
                                <div class="eva-top">
                                    <div class="tit">课程评分</div>
                                    <div class="star">
                                        <div class="score"><i>5</i></div>
                                    </div>
                                    <span class="star-score"> <i>5</i></span></div>
                                <div class="eva-cont">
                                    <div class="tit">学员评语</div>
                                    <div class="text-box">
                                        <textarea class="form-control" rows="5"
                                                  placeholder="扯淡、吐槽、表扬、鼓励......想说啥说啥!"></textarea>
                                        <div class="text-right"><span>发表评论</span></div>
                                    </div>
                                </div>
                            </div>
                            <div class="course-evaluate">
                                <div class="top-tit">评论
                                    <span>
                        <label><input name="eval" type="radio" value="" checked/> 所有学生 </label>
                        <label><input name="eval" type="radio" value=""/> 完成者 </label>
                    </span>
                                </div>
                                <div class="top-cont">
                                    <div class="cont-top-left">
                                        <div class="star-scor">
                                            <div class="star-show">
                                                <div class="score"><i>5</i></div>
                                            </div>
                                            <div class="scor">4.9分</div>
                                        </div>
                                        <div class="all-scor">总评分:12343</div>
                                    </div>
                                    <div class="cont-top-right">
                                        <div class="star-grade">五星
                                            <div class="grade">
                                                <div class="grade-percent"><span></span></div>
                                                <div class="percent-num"><i>95</i>%</div>
                                            </div>
                                        </div>
                                        <div class="star-grade">四星
                                            <div class="grade">
                                                <div class="grade-percent"><span></span></div>
                                                <div class="percent-num"><i>5</i>%</div>
                                            </div>
                                        </div>
                                        <div class="star-grade">三星
                                            <div class="grade">
                                                <div class="grade-percent"><span></span></div>
                                                <div class="percent-num"><i>0</i>%</div>
                                            </div>
                                        </div>
                                        <div class="star-grade">二星
                                            <div class="grade">
                                                <div class="grade-percent"><span></span></div>
                                                <div class="percent-num"><i>2</i>%</div>
                                            </div>
                                        </div>
                                        <div class="star-grade">一星
                                            <div class="grade">
                                                <div class="grade-percent"><span></span></div>
                                                <div class="percent-num"><i>1</i>%</div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="comment-item-box">
                                    <div class="title">评论 <span>12453条评论</span></div>
                                    <div class="item">
                                        <div class="item-left">
                                            <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                            <p>毛老师</p>
                                        </div>
                                        <div class="item-cent">
                                            <p>
                                                很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                            <p class="time">2017-2-43</p>
                                        </div>
                                        <div class="item-rit">
                                            <p>
                                            <div class="star-show">
                                                <div class="score"><i>4</i></div>
                                            </div>
                                            </p>
                                            <p>评分 <span>5星</span></p>
                                        </div>
                                    </div>
                                    <div class="item">
                                        <div class="item-left">
                                            <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                            <p>毛老师</p>
                                        </div>
                                        <div class="item-cent">
                                            <p>
                                                很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                            <p class="time">2017-2-43</p>
                                        </div>
                                        <div class="item-rit">
                                            <p>
                                            <div class="star-show">
                                                <div class="score"><i>5</i></div>
                                            </div>
                                            </p>
                                            <p>评分 <span>5星</span></p>
                                        </div>
                                    </div>
                                    <div class="item">
                                        <div class="item-left">
                                            <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                            <p>毛老师</p>
                                        </div>
                                        <div class="item-cent">
                                            <p>
                                                很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                            <p class="time">2017-2-43</p>
                                        </div>
                                        <div class="item-rit">
                                            <p>
                                            <div class="star-show">
                                                <div class="score"><i>5</i></div>
                                            </div>
                                            </p>
                                            <p>评分 <span>5星</span></p>
                                        </div>
                                    </div>
                                    <div class="item">
                                        <div class="item-left">
                                            <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                            <p>毛老师</p>
                                        </div>
                                        <div class="item-cent">
                                            <p>
                                                很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                            <p class="time">2017-2-43</p>
                                        </div>
                                        <div class="item-rit">
                                            <p>
                                            <div class="star-show">
                                                <div class="score"><i>5</i></div>
                                            </div>
                                            </p>
                                            <p>评分 <span>5星</span></p>
                                        </div>
                                    </div>
                                    <div class="get-more">页面加载中...</div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <!--侧边栏-->
                    <!--#include virtual="/include/course_detail_side.html"-->
                    <!--侧边栏-->
                </div>
            </div>
        </div>
    </div>
    <div class="popup-course">
        <div class="mask"></div>
        <!--欢迎访问课程弹窗- start -->
        <div class="popup-course-box">
            <div class="title">${model.courseBase.name} <span class="close-popup-course-box">×</span></div>
            <div class="content">
                <p>欢迎学习本课程,本课程免费您可以立即学习,也可加入我的课程表享受更优质的服务。</p>
                <p><a href="#" @click.prevent="addCourseTable()">加入我的课程表</a> <a href="#"
                                                                                @click.prevent="startLearngin()">立即学习</a>
                </p>
            </div>
        </div>
    </div>
    <div class="popup-box">
        <div class="mask"></div>
        <!--支付弹窗- start -->
        <div class="popup-pay-box">
            <div class="title">${model.courseBase.name} <span class="close-popup-pay-box">×</span></div>
            <div class="content">
                <img :src="qrcode" width="200" height="200" alt="请点击支付宝支付按钮,并完成扫码支付。"/>

                <div class="info">
                    <p class="info-tit">${model.courseBase.name}<span>课程有效期:${model.courseBase.validDays}天</span></p>
                    <p class="info-pic">课程价格 : <span>¥${model.courseBase.originalPrice!''}元</span></p>
                    <p class="info-new-pic">优惠价格 : <span>¥${model.courseBase.price!''}元</span></p>
                </div>
            </div>
            <div class="fact-pic">实际支付: <span>¥${model.courseBase.price!''}元</span></div>
            <div class="go-pay"><a href="#" @click.prevent="wxPay()">微信支付</a><a href="#"
                                                                                @click.prevent="aliPay()">支付宝支付</a><a
                        href="#" @click.prevent="querypayresult()">支付完成</a><a href="#" @click.prevent="startLearngin()">试学</a>
            </div>
        </div>
        <!--支付弹窗- end -->
        <div class="popup-comment-box">

        </div>
    </div>
    <!-- 页面底部 -->
    <!--底部版权-->
    <!--#include virtual="/include/footer.html"-->
    <!--底部版权-->
</div>
<script>var courseId = "${model.courseBase.id}";
    var courseCharge = "${model.courseBase.charge}"</script>
<!--#include virtual="/include/course_detail_dynamic.html"-->
</body>

1.4.5 测试

首先修改一下前端“.env”文件

image-20240114213703151

启动前后端项目进行联调

image-20240114223829465image-20240114223903093

1.5 课程预览 视频

我们在“课程预览”模块右侧有一个目录,点击目录之后可以查看对应的视频

image-20240114224326825

1.5.1 配置Nginx

从路径就能看出来,是公开的,不管用户是否登录都能访问

#openapi
location /open/content/ {
        proxy_pass http://gatewayserver/content/open/;
} 
location /open/media/ {
        proxy_pass http://gatewayserver/media/open/;
} 

重新加载一下配置文件

nginx.exe -s reload

1.5.2 CourseOpenController

在content-api模块创建此类

@Api(value = "课程公开查询接口", tags = "课程公开查询接口")
@RestController
@RequestMapping("/open")
public class CourseOpenController {

    @Autowired
    private CourseBaseInfoService courseBaseInfoService;

    @Autowired
    private CoursePublishService coursePublishService;

    /**
     * 根据课程的id查询课程的信息
     */
    @GetMapping("/course/whole/{courseId}")
    public CoursePreviewDto getPreviewInfo(@PathVariable("courseId") Long courseId) {
        //获取课程预览信息
        return coursePublishService.getCoursePreviewInfo(courseId);
    }

}

1.5.3 MediaOpenController

在media-api工程中创建此类

@Api(value = "媒资文件管理接口", tags = "媒资文件管理接口")
@RestController
@RequestMapping("/open")
public class MediaOpenController {

    @Autowired
    MediaFileService mediaFileService;

    @ApiOperation("预览文件")
    @GetMapping("/preview/{mediaId}")
    public RestResponse<String> getPlayUrlByMediaId(@PathVariable String mediaId) {

        MediaFiles mediaFiles = mediaFileService.getFileById(mediaId);
        if (mediaFiles == null || StringUtils.isEmpty(mediaFiles.getUrl())) {
            XueChengPlusException.cast("视频还没有转码处理");
        }
        return RestResponse.success(mediaFiles.getUrl());

    }


}
/**
 * 根据媒资id获取媒资信息
 */
@Override
public MediaFiles getFileById(String mediaId) {
    return mediaFilesMapper.selectById(mediaId);
}

1.5.4 测试

这些如果关联视频的话,都是可以播放的,但是我只关联了一个视频

image-20240114231858162

二、提交课程审核

在如下图所示区域

image-20240114232936421

2.1 需求分析

来自黑马程序员资料

课程发布前要先审核,审核通过方可发布

image-20240114233240683

为什么课程审核通过才可以发布呢

这样做为了防止课程信息有违规情况,课程信息不完善对网站用户体验也不好,课程审核不仅起到监督作用,也是帮助教学机构规范使用平台的手段。

如何控制课程审核通过才可以发布课程呢

在课程基本表course_base表设置课程审核状态字段,包括:未提交、已提交(未审核)、审核通过、审核不通过。

下边是课程状态的转化关系

真的非常的形象

img

说明如下

1、一门课程新增后它的审核状为”未提交“,发布状态为”未发布“。

2、课程信息编辑完成,教学机构人员执行”提交审核“操作。此时课程的审核状态为”已提交“。

3、当课程状态为已提交时运营平台人员对课程进行审核。

4、运营平台人员审核课程,结果有两个:审核通过、审核不通过。

5、课程审核过后不管状态是通过还是不通过,教学机构可以再次修改课程并提交审核,此时课程状态为”已提交“。此时运营平台人员再次审核课程。

6、课程审核通过,教学机构人员可以发布课程,发布成功后课程的发布状态为”已发布“。

7、课程发布后通过”下架“操作可以更改课程发布状态为”下架“

8、课程下架后通过”上架“操作可以再次发布课程,上架后课程发布状态为“发布”。

2.2 数据模型

  • 课程提交审核后还允许修改课程吗

如果不允许修改是不合理的,因为提交审核后可以继续做下一个阶段的课程内容,比如添加课程计划,上传课程视频等。

如果允许修改那么课程审核时看到的课程内容从哪里来?如果也从课程基本信息表、课程营销表、课程计划表查询那么存在什么问题呢

运营人员审核课程和教学机构编辑课程操作的数据是同一份,此时会导致冲突。比如:运营人员正在审核时教学机构把数据修改了

image-20240114235145839

为了解决这个问题,专门设计课程预发布表

image-20240114235212529

提交课程审核,将课程信息汇总后写入课程预发布表,课程预发布表记录了教学机构在某个时间点要发布的课程信息。

课程审核人员从预发布表查询信息进行审核。

课程审核的同时可以对课程进行修改,修改的内容不会写入课程预发布表。

课程审核通过执行课程发布,将课程预发布表的信息写入课程发布表

相当于此时用户修改的内容和运营人员审核的内容给区分开了

  • 提交审核课程后,也修改了课程信息,可以再次提交审核吗

是可以的,只不过提交审核课程后,必须等到课程审核完成才可以再次提交课程

image-20240114235322689

  • 表结构

提交审核将信息写入课程预发布表,课程预发布表course_publish_pre结构如下

预发布表其实就是课程营销信息表、课程师资表、课程计划表、课程基本信息表的大综合

当预发布表的数据审核通过后,就会将预发布表的数据拷贝一份到发布表中,用户发布的内容其实就是发布表中的内容

image-20240114235421252

image-20240115000524773

更新课程基本信息表的课程审核状态为:已经提交

课程审核后更新课程基本信息表的审核状态、课程预发布表的审核状态,并将审核结果写入课程审核记录。

审核记录表结构如下

img

2.2.1 课程预发布表 course_publish_pre

@Data
@TableName("course_publish_pre")
public class CoursePublishPre implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    private Long id;

    /**
     * 机构ID
     */
    private Long companyId;

    /**
     * 公司名称
     */
    private String companyName;

    /**
     * 课程名称
     */
    private String name;

    /**
     * 适用人群
     */
    private String users;

    /**
     * 标签
     */
    private String tags;

    /**
     * 创建人
     */
    private String username;

    /**
     * 大分类
     */
    private String mt;

    /**
     * 大分类名称
     */
    private String mtName;

    /**
     * 小分类
     */
    private String st;

    /**
     * 小分类名称
     */
    private String stName;

    /**
     * 课程等级
     */
    private String grade;

    /**
     * 教育模式
     */
    private String teachmode;

    /**
     * 课程图片
     */
    private String pic;

    /**
     * 课程介绍
     */
    private String description;

    /**
     * 课程营销信息,json格式
     */
    private String market;

    /**
     * 所有课程计划,json格式
     */
    private String teachplan;

    /**
     * 教师信息,json格式
     */
    private String teachers;

    /**
     * 提交时间
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate;

    /**
     * 审核时间
     */
    private LocalDateTime auditDate;

    /**
     * 状态
     */
    private String status;

    /**
     * 备注
     */
    private String remark;

    /**
     * 收费规则,对应数据字典--203
     */
    private String charge;

    /**
     * 现价
     */
    private Float price;

    /**
     * 原价
     */
    private Float originalPrice;

    /**
     * 课程有效期天数
     */
    private Integer validDays;


}

2.3 CoursePublishController

1、查询课程基本信息、课程营销信息、课程计划信息等课程相关信息,整合为课程预发布信息。

2、向课程预发布表course_publish_pre插入一条记录,如果已经存在则更新,审核状态为:已提交。

3、更新课程基本表course_base课程审核状态为:已提交。

约束:

1、对已提交审核的课程不允许提交审核。

2、本机构只允许提交本机构的课程。

3、没有上传图片不允许提交审核。

4、没有添加课程计划不允许提交审核。

使用代码生成器生成课程发布表、课程预发布表的PO、Mpper,并拷贝到相应的工程下。

首先在content-api工程下添加如下接口

 @ResponseBody
@PostMapping ("/courseaudit/commit/{courseId}")
public void commitAudit(@PathVariable("courseId") Long courseId){
     Long companyId = 1232141425L;
     coursePublishService.commitAudit(companyId,courseId);

 }

2.4 CoursePublishServiceImpl

/**
     * @description 提交审核
     * @param courseId  课程id
     */
    @Transactional
    @Override
    public void commitAudit(Long companyId, Long courseId) {
        // 课程基本信息
        CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId);
        if (courseBaseInfo == null) {
            XueChengPlusException.cast("课程找不到");
        }
        //得到审核状态
        String auditStatus  = courseBaseInfo.getAuditStatus();
        //TODO 如果课程的审核状态为已提交则不允许提交
        if ("202003".equals(auditStatus )) {
            XueChengPlusException.cast("课程已经提交申请,请您耐心等待审核");
        }

        //TODO 本机构只能提交本机构的课程

        //TODO 如果课程的图片、计划信息没有填写不允许提交
        if (StringUtils.isEmpty(courseBaseInfo.getPic())) {
            XueChengPlusException.cast("请上传课程图片");
        }
        //查询课程计划
        CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId);

        if (coursePreviewInfo == null || coursePreviewInfo.getTeachplans().size() == 0) {
            XueChengPlusException.cast("请上传课程计划");
        }

        //TODO 1.查询到课程基本信息、营销信息、计划等信息插入到课程预报布表
        CoursePublishPre coursePublishPre = new CoursePublishPre();

        //TODO 1.1课程基本信息加部分营销信息
        BeanUtils.copyProperties(courseBaseInfo,coursePublishPre);
        // 设置机构id
        coursePublishPre.setCompanyId(companyId);
        //课程营销信息
        CourseMarket courseMarket = courseMarketMapper.selectById(courseId);
        //转为json
        String courseMarketJson = JSON.toJSONString(courseMarket);
        //将课程营销信息json数据放入课程预发布表
        coursePublishPre.setMarket(courseMarketJson);

        //TODO 1.2查询课程计划信息
        List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId);
        if(teachplanTree.size()<=0){
            XueChengPlusException.cast("提交失败,还没有添加课程计划");
        }
        //转json
        String teachplanTreeString = JSON.toJSONString(teachplanTree);
        coursePublishPre.setTeachplan(teachplanTreeString);

        //TODO 1.3设置预发布记录状态,已提交
        coursePublishPre.setStatus("202003");
        //教学机构id
        coursePublishPre.setCompanyId(companyId);
        //提交时间
        coursePublishPre.setCreateDate(LocalDateTime.now());
        CoursePublishPre coursePublishPreUpdate = coursePublishPreMapper.selectById(courseId);
        if(coursePublishPreUpdate == null){
            //添加课程预发布记录
            coursePublishPreMapper.insert(coursePublishPre);
        }else{
            coursePublishPreMapper.updateById(coursePublishPre);
        }

        //TODO 2.更新课程基本信息表的审核状态为已提交
        CourseBase courseBase = courseBaseMapper.selectById(courseId);
        // 审核状态为已提交
        courseBase.setAuditStatus("202003");
        courseBaseMapper.updateById(courseBase);
    }

补充课程审核状态码

[
    {
        "code":"202001",
        "desc":"审核未通过"
    },
    {
        "code":"202002",
        "desc":"未提交"
    },
    {
        "code":"202003",
        "desc":"已提交"
    },
    {
        "code":"202004",
        "desc":"审核通过"
    }
]

课程发布状态

[
    {
        "code":"203001",
        "desc":"未发布"
    },
    {
        "code":"203002",
        "desc":"已发布"
    },
    {
        "code":"203003",
        "desc":"下线"
    }
]

2.5 测试

首次提交审核

image-20240115004820948

image-20240115004839738

image-20240115010455885

因为我们没有做审核模块,所以我们直接修改数据库的数据即可,作为审核通过

1、修改课程预发布表的状态status字段为审核通过202004。

2、修改课程基本表的审核状态auditStatus字段为审核通过202004。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1387345.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SSM的社区老年人关怀服务系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

关于steam游戏搬砖,想给大家的几点忠告

关于CSGO游戏搬砖&#xff0c;想给大家的几点忠告&#xff1a; 1、新出的箱子&#xff0c;里面开出的皮肤短时间内会溢价&#xff0c;价格虚高&#xff0c;后期会呈逐渐下跌趋势&#xff0c;这就是我们不让大家碰新品的原因&#xff0c;哪怕利润再高也不建议购入或者囤货&…

微笑抑郁症测试

微笑抑郁症属于抑郁症中的一类&#xff0c;微笑和抑郁本是两个对立词汇&#xff0c;但是可以并存且多发于职场精英中&#xff0c;由于工作关系他们必须保持这微信&#xff0c;让他人看到自己最美好的一面&#xff0c;过度压抑内心的情绪&#xff0c;如&#xff1a;工作压力&…

Java开发笔记

一、参数校验 1、校验json字符串是否符合规范 &#xff08;1&#xff09;业务场景&#xff1a;接收前端传输过来的json串&#xff0c;需要将其写入数据库&#xff0c;写入之前需要校验其是否能够转换成对应实体类&#xff0c;以便后续从数据库读取   &#xff08;2&#xff0…

用Pytorch实现线性回归模型

目录 回顾Pytorch实现步骤1. 准备数据2. 设计模型class LinearModel代码 3. 构造损失函数和优化器4. 训练过程5. 输出和测试完整代码 练习 回顾 前面已经学习过线性模型相关的内容&#xff0c;实现线性模型的过程并没有使用到Pytorch。 这节课主要是利用Pytorch实现线性模型。…

什么是TestNG以及如何创建testng.xml文件?

目录 什么是TestNG&#xff1f; 如何创建testng.xml文件 手动创建testng.xml 通过testng.xml运行整个包 通过testng.xml运行类 使用Eclipse创建testng.xml 本文将讨论TestNG以及如何通过执行testng.xml文件在TestNG中运行第一个测试用例。 什么是TestNG&#xff1f; Te…

2.1.2 一个关于y=ax+b的故事

跳转到根目录&#xff1a;知行合一&#xff1a;投资篇 已完成&#xff1a; 1、投资&技术   1.1.1 投资-编程基础-numpy   1.1.2 投资-编程基础-pandas   1.2 金融数据处理   1.3 金融数据可视化 2、投资方法论   2.1.1 预期年化收益率   2.1.2 一个关于yaxb的…

Unity游戏图形学 Shader结构

shader结构 shader语言 openGL&#xff1a;SLG跨平台 >GLSL&#xff1a;openGL shaderlauguge DX&#xff1a;微软开发&#xff0c;性能很好&#xff0c;但是不能跨平台 >HLSL&#xff1a;high level shader language CG&#xff1a;微软和Nvidia公司联合开发&#xff…

2024年腾讯云新用户优惠云服务器价格多少?

腾讯云服务器租用价格表&#xff1a;轻量应用服务器2核2G3M价格62元一年、2核2G4M价格118元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;2核4G5M带宽756元三年、轻量4核8G12M服务器446元一年、646元15个月&#xff0c;云服务器CVM S5实例2核2G配置280.8元一年…

统计学-R语言-3

文章目录 前言给直方图增加正态曲线的不恰当之处直方图与条形图的区别核密度图时间序列图洛伦茨曲线计算绘制洛伦茨曲线所需的各百分比数值绘制洛伦茨曲线 练习 前言 本篇文章是介绍对数据的部分图形可视化的图型展现。 给直方图增加正态曲线的不恰当之处 需要注意的是&#…

项目解决方案:多个分厂的视频监控汇聚到总厂

目 录 1、概述 2、建设目标及需求 2.1 建设目标 2.2 需求描述 2.3 需求分析 3. 设计依据与设计原则 3.1 设计依据 3.2设计原则 1、先进性与适用性 2、经济性与实用性 3、可靠性与安全性 4、开放性 5、可扩充性 6、追求最优化的系统设备配置…

【数据结构】C语言实现共享栈

共享栈的C语言实现 导言一、共享栈1.1 共享栈的初始化1.2 共享栈的判空1.3 共享栈的入栈1.3.1 空指针1.3.2 满栈1.3.3 入栈空间错误1.3.4 正常入栈1.3.5 小结 1.4 共享栈的查找1.5 共享栈的出栈1.6 共享栈的销毁 二、共享栈的实现演示结语 导言 大家好&#xff0c;很高兴又和大…

JVM-Arthas高效的监控工具

一、arthas介绍 3.选择监控哪个进程 4.进入具体进程 二、arthas的基础命令与基本操作 1.查询包含Java的系统属性&#xff1a; 命令&#xff1a;sysprop |grep java 1.查询不含Java的系统属性&#xff1a; 命令&#xff1a;sysprop | grep -v java 3.打印历史命令 命令&#…

排序算法之八:计数排序

1.计数排序思想 计数排序&#xff0c;顾名思义就是计算数据的个数 计数排序又称非比较排序 思想&#xff1a;计数排序又称为鸽巢原理&#xff0c;是对哈希直接定址法的变形应用。 操作步骤&#xff1a; 统计相同元素出现次数 根据统计的结果将序列回收到原来的序列中 计数…

20240115如何在线识别俄语字幕?

20240115如何在线识别俄语字幕&#xff1f; 2024/1/15 21:25 百度搜索&#xff1a;俄罗斯语 音频 在线识别 字幕 Bilibili&#xff1a;俄语AI字幕识别 音视频转文字 字幕小工具V1.2 BING&#xff1a;音视频转文字 字幕小工具V1.2 https://www.bilibili.com/video/BV1d34y1F7…

嵌入式软件工程师面试题——2025校招社招通用(十八)

说明&#xff1a; 面试群&#xff0c;群号&#xff1a; 228447240面试题来源于网络书籍&#xff0c;公司题目以及博主原创或修改&#xff08;题目大部分来源于各种公司&#xff09;&#xff1b;文中很多题目&#xff0c;或许大家直接编译器写完&#xff0c;1分钟就出结果了。但…

sqli-labs关卡23(基于get提交的过滤注释符的联合注入)

文章目录 前言一、回顾前几关知识点二、靶场第二十三关通关思路1、判断注入点2、爆数据库名3、爆数据库表4、爆数据库列5、爆数据库关键信息 总结 前言 此文章只用于学习和反思巩固sql注入知识&#xff0c;禁止用于做非法攻击。注意靶场是可以练习的平台&#xff0c;不能随意去…

SQL-用户管理与用户权限

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

【JupyterLab】在 conda 虚拟环境中 JupyterLab 的安装与使用

【JupyterLab】在 conda 虚拟环境中 JupyterLab 的安装与使用 1 JupyterLab 介绍2 安装2.1 Jupyter Kernel 与 conda 虚拟环境 3 使用3.1 安装中文语言包(Optional)3.2 启动3.3 常用快捷键3.3.1 命令模式下 3.4 远程访问个人计算机3.4.1 局域网下 1 JupyterLab 介绍 官方文档: …