内容管理模块 - 课程预览、提交审核
文章目录
- 内容管理模块 - 课程预览、提交审核
- 一、课程预览
- 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 测试
一、课程预览
课程预览就是把课程的相关信息进行整合,在课程预览界面进行展示,课程预览界面与课程发布的课程详情界面一致
1.1 需求分析
客户可以通过课程预览页面查看信息是否存在问题
如下课程预览的数据来源
下图显示了整个课程预览的流程图
也就是说现在怎么给前端返回一个页面呢?
最最最原始的JSP可以,一些模板引擎技术也可以
前端请求内容管理服务后,后台服务系统要返回一个页面
最终要返回的页面如下图所示(预览页面,如果客户成功确认发布后,发布后的页面和预览时的页面相同)
说明如下:
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技术就是一种模板引擎技术
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;
}
}
测试效果
还是能展示出来的
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端口,如果出现下图所示的界面,说明端口问题已经被解决了
假如说按照1.3.1设置了Nginx配置的话,打开网页会是如下页面
主要访问一下预览界面http://www.51xuecheng.cn/course/course_template.html
1.3.3 配置host文件
C:\Windows\System32\drivers\etc目录下有一个host文件
将下面的内容粘贴到里面即可
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代理,通过文件服务域名统一访问。如下图
我们使用的分布式文件系统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;
}
}
配置完毕,重新加载nginx配置文件
通过cmd进入nginx.exe所在目录,运行如下命令
nginx.exe -s reload
访问:http://file.51xuecheng.cn/mediafiles/2022/09/13/a16da7a132559daf9e1193166b3e7f52.jpg
如下所示效果
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;
}
重新加载配置文件
之后
下边需要配置learning.html页面的视频播放路径来测试视频播放页面,找到learning.html页面中videoObject对象的定义处,配置视频的播放地址
换成minio系统中一个可以播放的地址
1.4 课程预览 主页
课程预览接口要将课程信息进行整合,在服务端渲染页面后返回浏览器。
下边对课程预览接口进行分析:
1、请求参数
传入课程id,表示要预览哪一门课程。
2、响应结果
输出课程详情页面到浏览器
响应页面到浏览器使用freemarker模板引擎技术实现,首先从课程资料目录下获取课程预览页面course_template.html,拷贝至内容管理的接口工程的resources/templates下,并将其在本目录复制一份命名为course_template.ftl
也就是说下图中的整个页面都是模板内容
其实就是下图所示的四部分信息
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
访问地址: http://www.51xuecheng.cn/api/content/coursepreview/74
出现的场景如下图所示
现在的请求其实是下面所示的流程:
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>
<!--侧边栏–>
<!--#include virtual="/include/course_detail_side.html"–>
<!--侧边栏–>
</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”文件
启动前后端项目进行联调
1.5 课程预览 视频
我们在“课程预览”模块右侧有一个目录,点击目录之后可以查看对应的视频
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 测试
这些如果关联视频的话,都是可以播放的,但是我只关联了一个视频
二、提交课程审核
在如下图所示区域
2.1 需求分析
来自黑马程序员资料
课程发布前要先审核,审核通过方可发布
为什么课程审核通过才可以发布呢?
这样做为了防止课程信息有违规情况,课程信息不完善对网站用户体验也不好,课程审核不仅起到监督作用,也是帮助教学机构规范使用平台的手段。
如何控制课程审核通过才可以发布课程呢?
在课程基本表course_base表设置课程审核状态字段,包括:未提交、已提交(未审核)、审核通过、审核不通过。
下边是课程状态的转化关系:
真的非常的形象
说明如下:
1、一门课程新增后它的审核状为”未提交“,发布状态为”未发布“。
2、课程信息编辑完成,教学机构人员执行”提交审核“操作。此时课程的审核状态为”已提交“。
3、当课程状态为已提交时运营平台人员对课程进行审核。
4、运营平台人员审核课程,结果有两个:审核通过、审核不通过。
5、课程审核过后不管状态是通过还是不通过,教学机构可以再次修改课程并提交审核,此时课程状态为”已提交“。此时运营平台人员再次审核课程。
6、课程审核通过,教学机构人员可以发布课程,发布成功后课程的发布状态为”已发布“。
7、课程发布后通过”下架“操作可以更改课程发布状态为”下架“
8、课程下架后通过”上架“操作可以再次发布课程,上架后课程发布状态为“发布”。
2.2 数据模型
- 课程提交审核后还允许修改课程吗?
如果不允许修改是不合理的,因为提交审核后可以继续做下一个阶段的课程内容,比如添加课程计划,上传课程视频等。
如果允许修改那么课程审核时看到的课程内容从哪里来?如果也从课程基本信息表、课程营销表、课程计划表查询那么存在什么问题呢?
运营人员审核课程和教学机构编辑课程操作的数据是同一份,此时会导致冲突。比如:运营人员正在审核时教学机构把数据修改了
为了解决这个问题,专门设计课程预发布表
提交课程审核,将课程信息汇总后写入课程预发布表,课程预发布表记录了教学机构在某个时间点要发布的课程信息。
课程审核人员从预发布表查询信息进行审核。
课程审核的同时可以对课程进行修改,修改的内容不会写入课程预发布表。
课程审核通过执行课程发布,将课程预发布表的信息写入课程发布表
相当于此时用户修改的内容和运营人员审核的内容给区分开了
- 提交审核课程后,也修改了课程信息,可以再次提交审核吗?
是可以的,只不过提交审核课程后,必须等到课程审核完成才可以再次提交课程
- 表结构
提交审核将信息写入课程预发布表,课程预发布表course_publish_pre结构如下:
预发布表其实就是课程营销信息表、课程师资表、课程计划表、课程基本信息表的大综合
当预发布表的数据审核通过后,就会将预发布表的数据拷贝一份到发布表中,用户发布的内容其实就是发布表中的内容
更新课程基本信息表的课程审核状态为:已经提交
课程审核后更新课程基本信息表的审核状态、课程预发布表的审核状态,并将审核结果写入课程审核记录。
审核记录表结构如下:
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 测试
首次提交审核
因为我们没有做审核模块,所以我们直接修改数据库的数据即可,作为审核通过
1、修改课程预发布表的状态status字段为审核通过202004。
2、修改课程基本表的审核状态auditStatus字段为审核通过202004。