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

发布时间:2024年01月15日

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

一、课程预览

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

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。

文章来源:https://blog.csdn.net/weixin_51351637/article/details/135612734
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。