部署静态页面
相关操作:https://mx67xggunk5.feishu.cn/wiki/FLozwxrrxihTJbkyTHgchDt4nUc
nginx的最终配置文件:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream fileserver{
server 192.168.101.65:9000 weight=10;
}
server {
listen 80;
server_name file.51xuecheng.cn;
ssi on;
ssi_silent_errors on;
location /video {
proxy_pass http://fileserver;
}
location /mediafiles {
proxy_pass http://fileserver;
}
}
server {
listen 80;
server_name www.51xuecheng.cn localhost;
ssi on;
ssi_silent_errors on;
location / {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/;
index index.html index.htm;
}
location /static/img/ {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/img/;
}
location /static/css/ {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/css/;
}
location /static/js/ {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/js/;
}
location /static/plugins/ {
alias D:/Javanode/newxczx/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:/Javanode/newxczx/xc-ui-pc-static-portal/plugins/;
}
location /course/preview/learning.html {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/course/learning.html;
}
location /course/search.html {
root D:/Javanode/newxczx/xc-ui-pc-static-portal;
}
location /course/learning.html {
root D:/Javanode/newxczx/xc-ui-pc-static-portal;
}
}
}
课程预览
接口开发
课程预览接口层:
由于要向模版中插入数据因此创建提个DTO进行存储:
@Data
public class CoursePreviewDto {
//课程基本信息和课程营销信息
CourseBaseInfoDto courseBase;
//课程计划
List<TeachplanDto> teachplans;
//TODO 课程师资信息
}
/**
* 课程预览
* @param courseId
* @return
*/
@GetMapping("/coursepreview/{courseId}")
public ModelAndView preview(@PathVariable("courseId") Long courseId){
CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("model",coursePreviewInfo);
modelAndView.setViewName("course_template");
return modelAndView;
}
service层:
/**
* @description 获取课程预览信息
* @param courseId 课程id
* @return com.xuecheng.content.model.dto.CoursePreviewDto
* @author Mr.M
* @date 2022/9/16 15:36
*/
public CoursePreviewDto getCoursePreviewInfo(Long courseId);
@Override
public CoursePreviewDto getCoursePreviewInfo(Long courseId) {
//查询数据库
CoursePreviewDto coursePreviewInfo = new CoursePreviewDto();
//查询课程详细信息
CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId);
coursePreviewInfo.setCourseBase(courseBaseInfo);
//查询课程计划
List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId);
coursePreviewInfo.setTeachplans(teachplanTree);
return coursePreviewInfo;
}
获取课程相关信息
课程预览界面的模版插槽(使用了freemark的模版插槽技术):将查询到的数据填充到代码中返回给前端,由于需要调用css等静态资源所以使用nginx服务器进行代理,将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>
nginx的配置文件:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream fileserver{
server 192.168.101.65:9000 weight=10;
}
#后台网关
upstream gatewayserver{
server 127.0.0.1:63010 weight=10;
}
server {
listen 80;
server_name file.51xuecheng.cn;
ssi on;
ssi_silent_errors on;
location /video {
proxy_pass http://fileserver;
}
location /mediafiles {
proxy_pass http://fileserver;
}
}
server {
listen 80;
server_name www.51xuecheng.cn localhost;
ssi on;
ssi_silent_errors on;
location / {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/;
index index.html index.htm;
}
location /static/img/ {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/img/;
}
location /static/css/ {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/css/;
}
location /static/js/ {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/js/;
}
location /static/plugins/ {
alias D:/Javanode/newxczx/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:/Javanode/newxczx/xc-ui-pc-static-portal/plugins/;
}
location /course/preview/learning.html {
alias D:/Javanode/newxczx/xc-ui-pc-static-portal/course/learning.html;
}
location /course/search.html {
root D:/Javanode/newxczx/xc-ui-pc-static-portal;
}
location /course/learning.html {
root D:/Javanode/newxczx/xc-ui-pc-static-portal;
}
location /open/content/ {
proxy_pass http://gatewayserver/content/open/;
}
location /open/media/ {
proxy_pass http://gatewayserver/media/open/;
}
#api
location /api/ {
proxy_pass http://gatewayserver/;
}
}
}
同时该配置文件中还配置了网关,所有的请求都要通过网关服务进行转发。
视屏播放和获取课程计划信息
由于课程预览界面要播放视屏和获取课程分章节信息
课程视屏接口:
package com.xuecheng.media.api;
import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.base.model.RestResponse;
import com.xuecheng.media.model.po.MediaFiles;
import com.xuecheng.media.service.MediaFileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@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查询文件信息
* @param mediaId
* @return
*/
MediaFiles getFileById(String mediaId);
/**
* 根据文件id得到文件信息
* @param mediaId
* @return
*/
@Override
public MediaFiles getFileById(String mediaId) {
MediaFiles mediaFiles = mediaFilesMapper.selectById(mediaId);
return mediaFiles;
}
获取课程计划信息:
package com.xuecheng.content.api;
import com.xuecheng.content.model.dto.CoursePreviewDto;
import com.xuecheng.content.service.CourseBaseInfoService;
import com.xuecheng.content.service.CoursePublishService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(value = "课程公开查询接口",tags = "课程公开查询接口")
@RestController
@RequestMapping("/open")
public class CourseOpenController {
@Autowired
private CourseBaseInfoService courseBaseInfoService;
@Autowired
private CoursePublishService coursePublishService;
@GetMapping("/course/whole/{courseId}")
public CoursePreviewDto getPreviewInfo(@PathVariable("courseId") Long courseId) {
//获取课程预览信息
CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId);
return coursePreviewInfo;
}
}
/**
* @description 获取课程预览信息
* @param courseId 课程id
* @return com.xuecheng.content.model.dto.CoursePreviewDto
* @author Mr.M
* @date 2022/9/16 15:36
*/
public CoursePreviewDto getCoursePreviewInfo(Long courseId);
package com.xuecheng.content.service.impl;
import com.xuecheng.content.model.dto.CourseBaseInfoDto;
import com.xuecheng.content.model.dto.CoursePreviewDto;
import com.xuecheng.content.model.dto.TeachplanDto;
import com.xuecheng.content.service.CourseBaseInfoService;
import com.xuecheng.content.service.CoursePublishService;
import com.xuecheng.content.service.TeachplanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CoursePublishServiceImpl implements CoursePublishService {
@Autowired
private CourseBaseInfoService courseBaseInfoService;
@Autowired
private TeachplanService teachplanService;
@Override
public CoursePreviewDto getCoursePreviewInfo(Long courseId) {
//查询数据库
CoursePreviewDto coursePreviewInfo = new CoursePreviewDto();
//查询课程详细信息
CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId);
coursePreviewInfo.setCourseBase(courseBaseInfo);
//查询课程计划
List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId);
coursePreviewInfo.setTeachplans(teachplanTree);
return coursePreviewInfo;
}
}
由于添加了这两个接口需要修改nginx的配置文件
配置这两个接口的跳转信息。
location /open/content/ {
proxy_pass http://gatewayserver/content/open/;
}
location /open/media/ {
proxy_pass http://gatewayserver/media/open/;
}
上面的全部配置文件已经包括这个依赖。
提交课程审核
CoursePublishController:
/**
* 课程审核
* @param courseId
* @return
*/
@ResponseBody
@PostMapping("/courseaudit/commit/{courseId}")
public void commitAudit(@PathVariable("courseId") Long courseId){
//TODO 获取机构id
Long companyId = 1232141425L;
coursePublishService.commitAudit(courseId,companyId);
}
//课程预发布表
@Autowired
private CoursePublishPreMapper coursePublishPreMapper;
//课程基本信息复杂查询
@Autowired
private CourseBaseInfoService courseBaseInfoService;
//课程计划
@Autowired
private TeachplanService teachplanService;
//课程营销信息
@Autowired
private CourseMarketMapper courseMarketMapper;
//课程基本信息
@Autowired
private CourseBaseMapper courseBaseMapper;
/**
* 提交审核
* @param courseId 课程id
* @param companyId 机构id
*/
@Override
@Transactional
public void commitAudit(Long courseId, Long companyId) {
//根据课程id查询课程信息
CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId);
//根据课程信息进行校验
if(courseBaseInfo == null){
XueChengPlusException.cast("课程不存在");
}
if(!courseBaseInfo.getCompanyId().equals(companyId)){
XueChengPlusException.cast("不允许提交其它机构的课程");
}
if(courseBaseInfo.getAuditStatus().equals("202003")){
XueChengPlusException.cast("课程已提交审核,请等待审核结果");
}
//课程图片是否填写
if(StringUtils.isEmpty(courseBaseInfo.getPic())){
XueChengPlusException.cast("提交失败,请上传课程图片");
}
//查询课程的营销信息
CourseMarket courseMarket = courseMarketMapper.selectById(courseId);
if(courseMarket == null){
XueChengPlusException.cast("营销信息不存在");
}
//将课程营销信息转为JSON
String courseMarketJson = JSON.toJSONString(courseMarket);
//查询课程计划信息
List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId);
if(teachplanTree == null || teachplanTree.size() == 0){
XueChengPlusException.cast("课程计划为空");
}
//将课程计划信息转为JSON
String teachplanTreeJson = JSON.toJSONString(teachplanTree);
//封装数据
CoursePublishPre coursePublishPre = new CoursePublishPre();
BeanUtils.copyProperties(courseBaseInfo,coursePublishPre);
//设置预发布记录状态,已提交
coursePublishPre.setStatus("202003");
//将课程营销信息json数据放入课程预发布表
coursePublishPre.setMarket(courseMarketJson);
//将课程计划信息json数据放入课程预发布表
coursePublishPre.setTeachplan(teachplanTreeJson);
//教学机构id
coursePublishPre.setCompanyId(companyId);
//提交时间
coursePublishPre.setCreateDate(LocalDateTime.now());
//判断是否添加过
CoursePublishPre coursePublishPreUpdate = coursePublishPreMapper.selectById(courseId);
if(coursePublishPreUpdate == null){
//添加课程预发布记录
coursePublishPreMapper.insert(coursePublishPre);
}else{
coursePublishPreMapper.updateById(coursePublishPre);
}
//修改课程基本信息表中课程状态为202003-提交审核
CourseBase courseBase = courseBaseMapper.selectById(courseId);
courseBase.setAuditStatus("202003");
int update = courseBaseMapper.updateById(courseBase);
if(update <= 0){
XueChengPlusException.cast("修改课程状态失败");
}
}
课程发布
教学机构人员在课程审核通过后即可发布课程,课程发布后会公开展示在网站上供学生查看、选课和学习。
在网站上展示课程信息需要解决课程信息显示的性能问题,如果速度慢(排除网速)会影响用户的体验性。
如何去快速搜索课程?
打开课程详情页面仍然去查询数据库可行吗?
为了提高网站的速度需要将课程信息进行缓存,并且要将课程信息加入索引库方便搜索,下图显示了课程发布后课程信息的流转情况
1、向内容管理数据库的课程发布表存储课程发布信息,更新课程基本信息表中发布状态为已发布。
2、向Redis存储课程缓存信息。
3、向Elasticsearch存储课程索引信息。
4、请求分布文件系统存储课程静态化页面(即html页面),实现快速浏览课程详情页面。
课程发布表的数据来源于课程预发布表,它们的结构基本一样,只是课程发布表中的状态是课程发布状态,如下图:
redis中的课程缓存信息是将课程发布表中的数据转为json进行存储。
elasticsearch中的课程索引信息是根据搜索需要将课程名称、课程介绍等信息进行索引存储。
MinIO中存储了课程的静态化页面文件(html网页),查看课程详情是通过文件系统去浏览课程详情页面。
课程发布接口
/**
* 课程发布
* @param courseId
*/
@ApiOperation("课程发布")
@ResponseBody
@PostMapping ("/coursepublish/{courseId}")
public void coursepublish(@PathVariable("courseId") Long courseId){
Long companyId = 1232141425L;
coursePublishService.publish(companyId,courseId);
}
/**
* 课程发布
* @param companyId
* @param courseId
*/
@Override
public void publish(Long companyId, Long courseId) {
//约束校验
//查询课程预发布表
CoursePublishPre coursePublishPre = coursePublishPreMapper.selectById(courseId);
if(coursePublishPre == null){
XueChengPlusException.cast("请先提交课程审核,审核通过才可以发布");
}
//本机构只允许提交本机构的课程
if(!coursePublishPre.getCompanyId().equals(companyId)){
XueChengPlusException.cast("不允许提交其它机构的课程。");
}
//课程审核状态
String auditStatus = coursePublishPre.getStatus();
//审核通过方可发布
if(!"202004".equals(auditStatus)){
XueChengPlusException.cast("操作失败,课程审核通过方可发布。");
}
//保存课程发布信息
saveCoursePublish(courseId);
//保存消息表
saveCoursePublishMessage(courseId);
//删除课程预发布表对应记录
coursePublishPreMapper.deleteById(courseId);
}
/**
* @description 保存课程发布信息
* @param courseId 课程id
* @return void
* @author Mr.M
* @date 2022/9/20 16:32
*/
private void saveCoursePublish(Long courseId){
//整合课程发布信息
//查询课程预发布表
CoursePublishPre coursePublishPre = coursePublishPreMapper.selectById(courseId);
if(coursePublishPre == null){
XueChengPlusException.cast("课程预发布数据为空");
}
CoursePublish coursePublish = new CoursePublish();
//拷贝到课程发布对象
BeanUtils.copyProperties(coursePublishPre,coursePublish);
coursePublish.setStatus("203002");
CoursePublish coursePublishUpdate = coursePublishMapper.selectById(courseId);
if(coursePublishUpdate == null){
coursePublishMapper.insert(coursePublish);
}else{
coursePublishMapper.updateById(coursePublish);
}
//更新课程基本表的发布状态
CourseBase courseBase = courseBaseMapper.selectById(courseId);
courseBase.setStatus("203002");
courseBaseMapper.updateById(courseBase);
}
/**
* @description 保存消息表记录,稍后实现
* @param courseId 课程id
* @return void
* @author Mr.M
* @date 2022/9/20 16:32
*/
private void saveCoursePublishMessage(Long courseId){
}