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

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

文章目录

  • 内容管理模块 - 课程预览、提交审核
  • 一、课程预览
    • 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。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JVM工作原理与实战(十六):运行时数据区-Java虚拟机栈

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、运行时数据区 二、Java虚拟机栈 1.栈帧的组成 2.局部变量表 3.操作数栈 4.帧数据 总结 前言 JVM作为Java程序的运行环境&#xff0c;其负责解释和执行字节码&#xff0c;管理…

存储的基本架构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、存储的需求背景二、自下而上存储架构总结 一、存储的需求背景 1、人的身份信息需要存储 这种信息可以用关系型数据库&#xff0c;例如mysql&#xff0c;那种表…

多线程并发与并行

&#x1f4d1;前言 本文主要是【并发与并行】——并发与并行的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&…

【JavaScript】事件监听:键盘事件

目录 一、keydown&#xff1a;按下键盘上的任意键时触发。 二、keyup&#xff1a;释放键盘上的任意键时触发。 三、keypress&#xff1a;在按下并释放能够产生字符的键时触发&#xff08;不包括功能键等&#xff09;。 四、input&#xff1a;在文本输入框或可编辑元素的内容…

基本BGP配置试验 :配置 IBGP 和 EBGP

一、预习&#xff1a; BGP&#xff1a;Border Gateway Protocol 没有精妙的算法&#xff0c;但能承载大量的路由&#xff0c;它不生产路由&#xff0c;它是路由的搬运工 使用TCP做为传输层协议&#xff0c;端口号179&#xff0c;使用触发式路由更新 1. BGP路由…

用Pytorch实现线性回归模型

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

DNS主从服务器配置

主从服务器配置&#xff1a; &#xff08;1&#xff09;完全区域传送&#xff1a;复制整个区域文件 #主DNS服务器的配置【主dns服务器的ip地址为192.168.168.129】 #编辑DNS系统配置信息&#xff08;我这里写的增加的信息&#xff0c;源文件里面有很多内容&#xff09; [root…

(超详细)4-YOLOV5改进-添加ShuffleAttention注意力机制

1、在yolov5/models下面新建一个ShuffleAttention.py文件&#xff0c;在里面放入下面的代码 代码如下&#xff1a; import numpy as np import torch from torch import nn from torch.nn import init from torch.nn.parameter import Parameterclass ShuffleAttention(nn.…

今天吃什么小游戏(基于Flask框架搭建的简单应用程序,用于随机选择午餐选项。代码分为两部分:Python部分和HTML模板部分)

今天吃什么 一个简单有趣的外卖点饭网站&#xff0c;不知道吃什么的时候&#xff0c;都可以用它自动决定你要吃的&#xff0c;包括各种烧烤、火锅、螺蛳粉、刀削面、小笼包、麦当劳等午餐全部都在内。点击开始它会随意调出不同的午餐&#xff0c;点击停止就会挑选一个你准备要吃…

小红书家居博主报价?怎么和博主合作?

小红书上各式各样的家居博主层出不穷&#xff0c;这些博主不仅为粉丝提供了家居装修的灵感&#xff0c;更为品牌带来了巨大的商业价值。 在当下家居市场竞争激烈的环境中&#xff0c;品牌与家居博主合作已成为了营销策略中的重要一环。博主们庞大的粉丝群体、丰富的内容产出以…

腾讯云服务器多少钱?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元一年…

区间预测 | Matlab实现LSSVM-ABKDE的最小二乘支持向量机结合自适应带宽核密度估计多变量回归区间预测

区间预测 | Matlab实现LSSVM-ABKDE的最小二乘支持向量机结合自适应带宽核密度估计多变量回归区间预测 目录 区间预测 | Matlab实现LSSVM-ABKDE的最小二乘支持向量机结合自适应带宽核密度估计多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现…

【Spring 篇】SpringMVC的数据响应:编织美妙的返回乐章

在Web开发的舞台上&#xff0c;数据响应就如同一场美妙的音乐演奏&#xff0c;而SpringMVC作为这场音乐的指挥者&#xff0c;如何优雅地将数据传递给前端&#xff0c;引发了无尽的思考和探索。本篇博客将带你走进SpringMVC的数据响应世界&#xff0c;解开其中的奥秘&#xff0c…

在windows11系统上利用docker搭建ubuntu记录

我的windows11系统上&#xff0c;之前已经安装好了window版本的docker&#xff0c;没有安装的小伙伴需要去安装一下。 下面直接记录安装linux的步骤&#xff1a; 一、创建linux容器 1、拉取镜像 docker pull ubuntu 2、查看镜像 docker images 3、创建容器 docker run --…

Kafka消费流程

Kafka消费流程 消息是如何被消费者消费掉的。其中最核心的有以下内容。 1、多线程安全问题 2、群组协调 3、分区再均衡 1.多线程安全问题 当多个线程访问某个类时&#xff0c;这个类始终都能表现出正确的行为&#xff0c;那么就称这个类是线程安全的。 对于线程安全&…

VTK开发调试环境下载(VTK开发环境一步到位直接开发,无需自己配置编译 VS2017+Qt5.12.10+VTK)

一、无与伦比的优势 直接下载代码就可以调试的VTK代码仓库。 二、资源制作原理 这个资源根据VTK源码 编译出动态库文件 pdb lib dll 文件&#xff08; x64 debug &#xff09; 并将这两者同时放在一个代码仓库里&#xff0c;下载就能用。 三、使用方法&#xff08;vtk-so…

如何结合主从复制,不停服情况下解决分库分表

首先我们要知道主从复制和分库分表两个概念&#xff0c;在此基础上可以将问题分为几个阶段来执行&#xff0c;参考了公众号 双写读老 双写双读 写新读新

为什么单片机上的程序不怎么使用malloc,而PC上经常使用?

为什么单片机上的程序不怎么使用malloc&#xff0c;而PC上经常使用&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿…

【新】Unity Meta Quest MR 开发(一):Passthrough 透视配置

文章目录 &#x1f4d5;教程说明&#x1f4d5;配置透视的串流调试功能&#x1f4d5;第一步&#xff1a;设置 OVRManager&#x1f4d5;第二步&#xff1a;添加 OVRPassthroughLayer 脚本&#x1f4d5;第三步&#xff1a;在场景中添加虚拟物体&#x1f4d5;第四步&#xff1a;设置…

2024年腾讯云服务器配置价格表(机型/磁盘/宽带/CPU)

腾讯云服务器租用价格表&#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元一年…